简介

Generator()函数是ES6提供的一种异步编程解决方案。
Generator()函数从语法上可以理解为是一个状态机,函数内部维护多个状态,函数执行的结果返回一个部署了Iterator接口的对象,通过这个对象可以依次获取Generator()函数内部的每一个状态。

特点

Generator()函数与普通函数区别:

  • function关键字与函数名之间有一个星号(*)。
  • 函数体内部使用yield关键字来定义不同的内部状态。
  • 必须要调用next()函数才会执行函数体 ```javascript function* helloworldGenerator() { console.log(‘Generator执行’);
    1. // 内部包含两个状态,hello与world。
    yield ‘hello’; yield ‘world’; }

const hw = helloworldGenerator(); console.log(‘这是测试执行先后顺序的语句’); hw.next(); // 必须等到执行next()函数才会执行函数体!!!

  1. <a name="i24OR"></a>
  2. ## yield表达式与next()函数的关系
  3. Generator()函数返回的是部署了Iterator接口的对象,而该对象是通过调用next()函数来遍历内部状态的,所以在没有调用下一轮next()函数时,函数处于暂停状态,而这个暂停状态就是通过yield表达式来体现的,因此Generator()函数对异步的控制是通过yield表达式来实现的。
  4. - next()函数的返回值是一个具有value和done属性的对象,next()函数调用后,如果遇到yield表达式,就会暂停后面的操作,并将yield表达式执行的结果作为value值进行返回,此时done属性的值为false。
  5. - 当再次执行next()函数时,会再继续往下执行,直到遇到下一个yield表达式。
  6. - 当所有的yield语句执行完毕时,会直接运行至函数末尾,如果有return语句,将return语句的表达式值作为value值返回;如果没有return语句,则value以undefined值进行返回,这两种情况下的done属性的值都为true,遍历结束。
  7. - yield语句本身没有返回值,如果将其赋给一个变量,则该变量的值为undefined。next()函数携带的参数可以作为上一轮yield表达式的返回值。
  8. ```javascript
  9. function* foo(x) {
  10. let y = 3 * (yield (x + 2))
  11. let z = yield (y / 4)
  12. return (x + y + z)
  13. }
  14. let a = foo(5)
  15. a.next() // { value:7, done:false }
  16. a.next() // { value:NaN, done:false }
  17. a.next() // { value:NaN, done:true }
  18. let b = foo(5)
  19. b.next() // { value:7, done:false }
  20. b.next(8) // { value:6, done:false }
  21. b.next(9) // { value:38, done:true }

注意点

1. 默认情况下不能使用new关键字

Generator()函数并不是构造函数,在默认情况下,不能使用new关键字。如果使用,则会抛出TypeError异常。

2. yield表达式会延迟执行

在Generator()函数中,yield表达式只有在调用next()函数时才会去执行,因此起到了延迟执行的效果。

3. yield表达式需要小括号括起来

当一个yield表达式出现在其他表达式中时,需要用小括号将yield表达式括起来,否则会抛出语法异常。

  1. function* demo() {
  2. console.log('Hello' + yield 123) // 抛出SyntaxError异常
  3. console.log('Hello' + (yield 123)) // 正确
  4. }

4. Generator的this特殊处理

在默认情况下,不能使用new关键字生成Generator的实例,因此Generator()函数中的this是无效的。使用call()函数改变Generator()函数的执行主体为Generator()函数的prototype属性,使得this指向原型属性,这样就可以访问到原型上添加的属性。

  1. function* testGenerator() {
  2. this.name = 'kingx'
  3. yield 'hello'
  4. yield 'world'
  5. }
  6. // 使用call()函数改变执行主体为testGenerator的prototype属性
  7. let t = testGenerator.call(testGenerator.prototype)
  8. t.next()
  9. console.log(t.name) // kingx

5. Generator()函数嵌套使用

如果在一个Generator()函数内部调用另一个Generator()函数,那么在不使用yield* 语句的情况下,需要手动遍历上一个Generator()函数,并在遍历完成后进入当前Generator()函数中。

  1. function* fn1() {
  2. yield 'test1';
  3. }
  4. function* fn2() {
  5. yield 'test2';
  6. // 调用另外一个Generator()函数,使用yield*关键字
  7. yield* fn1();
  8. yield 'test3';
  9. }
  10. let f = fn2();
  11. for (let key of f) {
  12. console.log(key);// test2 test1 test3
  13. }