简介
Generator()函数是ES6提供的一种异步编程解决方案。
Generator()函数从语法上可以理解为是一个状态机,函数内部维护多个状态,函数执行的结果返回一个部署了Iterator接口的对象,通过这个对象可以依次获取Generator()函数内部的每一个状态。
特点
Generator()函数与普通函数区别:
- function关键字与函数名之间有一个星号(*)。
- 函数体内部使用yield关键字来定义不同的内部状态。
- 必须要调用next()函数才会执行函数体
```javascript
function* helloworldGenerator() {
console.log(‘Generator执行’);
yield ‘hello’; yield ‘world’; }// 内部包含两个状态,hello与world。
const hw = helloworldGenerator(); console.log(‘这是测试执行先后顺序的语句’); hw.next(); // 必须等到执行next()函数才会执行函数体!!!
<a name="i24OR"></a>
## yield表达式与next()函数的关系
Generator()函数返回的是部署了Iterator接口的对象,而该对象是通过调用next()函数来遍历内部状态的,所以在没有调用下一轮next()函数时,函数处于暂停状态,而这个暂停状态就是通过yield表达式来体现的,因此Generator()函数对异步的控制是通过yield表达式来实现的。
- next()函数的返回值是一个具有value和done属性的对象,next()函数调用后,如果遇到yield表达式,就会暂停后面的操作,并将yield表达式执行的结果作为value值进行返回,此时done属性的值为false。
- 当再次执行next()函数时,会再继续往下执行,直到遇到下一个yield表达式。
- 当所有的yield语句执行完毕时,会直接运行至函数末尾,如果有return语句,将return语句的表达式值作为value值返回;如果没有return语句,则value以undefined值进行返回,这两种情况下的done属性的值都为true,遍历结束。
- yield语句本身没有返回值,如果将其赋给一个变量,则该变量的值为undefined。next()函数携带的参数可以作为上一轮yield表达式的返回值。
```javascript
function* foo(x) {
let y = 3 * (yield (x + 2))
let z = yield (y / 4)
return (x + y + z)
}
let a = foo(5)
a.next() // { value:7, done:false }
a.next() // { value:NaN, done:false }
a.next() // { value:NaN, done:true }
let b = foo(5)
b.next() // { value:7, done:false }
b.next(8) // { value:6, done:false }
b.next(9) // { value:38, done:true }
注意点
1. 默认情况下不能使用new关键字
Generator()函数并不是构造函数,在默认情况下,不能使用new关键字。如果使用,则会抛出TypeError异常。
2. yield表达式会延迟执行
在Generator()函数中,yield表达式只有在调用next()函数时才会去执行,因此起到了延迟执行的效果。
3. yield表达式需要小括号括起来
当一个yield表达式出现在其他表达式中时,需要用小括号将yield表达式括起来,否则会抛出语法异常。
function* demo() {
console.log('Hello' + yield 123) // 抛出SyntaxError异常
console.log('Hello' + (yield 123)) // 正确
}
4. Generator的this特殊处理
在默认情况下,不能使用new关键字生成Generator的实例,因此Generator()函数中的this是无效的。使用call()函数改变Generator()函数的执行主体为Generator()函数的prototype属性,使得this指向原型属性,这样就可以访问到原型上添加的属性。
function* testGenerator() {
this.name = 'kingx'
yield 'hello'
yield 'world'
}
// 使用call()函数改变执行主体为testGenerator的prototype属性
let t = testGenerator.call(testGenerator.prototype)
t.next()
console.log(t.name) // kingx
5. Generator()函数嵌套使用
如果在一个Generator()函数内部调用另一个Generator()函数,那么在不使用yield* 语句的情况下,需要手动遍历上一个Generator()函数,并在遍历完成后进入当前Generator()函数中。
function* fn1() {
yield 'test1';
}
function* fn2() {
yield 'test2';
// 调用另外一个Generator()函数,使用yield*关键字
yield* fn1();
yield 'test3';
}
let f = fn2();
for (let key of f) {
console.log(key);// test2 test1 test3
}