本篇文章以若干具体的生成器函数为例,逐步分析其 执行过程,详细展现生成器函数 内部执行流 和 外部控制流 之间的 消息传递机制,包括 普通消息传递、异常流传播 和生成器实例的 主动终结,最后简单展现 yield*
表达式的执行过程。javascript
Tips: 代码注释中的 >>
表示 console.log
打印的内容,->
表示函数调用返回的内容。java
yield
和 next()
yield ..
和next(..)
这一对组合起来,在生成器的执行过程当中构成一个双向消息传递系统es6——《你不知道的JavaScript》(中卷)express
function * genFunc() {
console.log('1st next()') // inner_1
console.log(yield 'yieldValue_1') // inner_2
console.log('2nd next()') // inner_3
console.log(yield 'yieldValue_2') // inner_4
console.log('3rd next()') // inner_5
return 'returnValue' // inner_6
}
// 调用 genFunc() 得到一个生成器实例
const gen = genFunc()
// gen.next() 可接收一个参数,无参调用等同于传入一个 undefined 参数
gen.next()
/** * 第一次调用 next(),启动 generator,开始执行 genFunc() 函数体代码 * inner_1: >> 1st next() * inner_2: 碰到 yield 语句 * 1. 计算 yield 后面的表达式,保存计算值 value * 2. 暂停执行 genFunc(),等待下一次 next() 调用 * 3. 这次 next() 调用返回 { value, done: false } * * -> { value: 'yieldValue_1', done: false } */
gen.next('nextArg_1')
/** * 恢复执行 genFunc() * inner_2: yield 接收 next() 传进来的参数 'nextArg_1', * 做为 yield 语句的计算值 * >> nextArg_1 * inner_3: >> 2nd next() * inner_4: 碰到 yield 语句 * 1. 计算 yield 后面的表达式,保存计算值 value * 2. 暂停执行 genFunc(),等待下一次 next() 调用 * 3. 这次 next() 调用返回 { value, done: false } * * -> { value: 'yieldValue_2', done: false } */
gen.next('nextArg_2')
/** * 恢复执行 genFunc() * inner_4: yield 接收 next() 传进来的参数 'nextArg_2', * 做为 yield 语句的计算值 * >> nextArg_1 * inner_5: >> 3rd next() * inner_6: 碰到 return 语句 * 1. 计算 return 语句后面的表达式,保存计算值 value * 2. genFunc() 执行结束 * 3. 这次 next() 调用返回 { value, done: true } * * 若 genFunc() 不含 return 语句,则当 genFunc 执行结束时, * 相应的 next() 调用返回 { value: undefined, done: false } * * -> { value: 'returnValue', done: true } */
gen.next('anyArgs')
/** * 此后每次 next() 调用都返回 { value: undefined, done: true } */
复制代码
next()
调用处流出若生成器函数内部抛出的异常未在函数内部被捕获,则该异常从相应的 next()
调用处流出。函数
function *genFunc() {
yield 'yieldValue' // inner_1
throw 'innerExceptionValue' // inner_2
return 'returnValue' // inner_3
}
const gen = genFunc()
gen.next()
/** * -> { value: 'yieldValue', done: false } */
try {
gen.next() // outer_1
} catch ('ouer catch:', value) { // outer_2
console.log(value) // outer_3
}
/** * outer_1: gen.next() 恢复 genFunc() 函数的执行, * inner_2: 抛出异常,该异常没有在 genFunc() 内部被捕获 * 1. genFunc() 异常结束 * 2. 生成器实例 gen 迭代结束,此后调用 gen.next() * 老是返回 { value: undefined, done: true } * outer_1: genFunc() 内部的抛出的异常从 gen.next() 流出 * outer_2: 距离最近的 catch 语句捕获了异常 * outer_3: >> outer catch: innerExceptionValue */
gen.next()
/** * -> { value: undefined, done: true } */
复制代码
Generator.prototype.throw()
抛出的异常从相应的 yield
处流出每一个生成器实例都从 Generator.prototype
继承了 throw()
方法。ui
gen.throw()
抛出的异常首先流入生成器函数内部,从相应的 yield
处流出。该异常可在生成器函数内部捕获。spa
function *genFunc() {
try {
yield 'yieldValue_1' // inner_1
} catch (value) { // inner_2
console.log('inner catch:', value) // inner_3
}
yield 'yieldValue_2' // inner_4
return 'returnValue' // inner_5
}
const gen = genFunc()
gen.next()
/** * -> { value: 'yieldValue_1', done: false } */
gen.throw('outerExceptionValue') // outer_1
/** * outer_1: * 1. gen.throw() 抛出异常 * 2. genFunc() 恢复执行 * 3. 异常流入 genFunc() 内部 * inner_1: 异常从 yield 语句流出 * inner_2: 距离最近的 catch 语句捕获该异常 * inner_3: >> inner catch: outerExceptionValue * inner_4: 碰到 yield 语句 * * -> { value: 'yieldValue_2', done: false } */
gen.next()
/** * -> { value: 'returnValue', done: true } */
复制代码
若 gen.throw()
抛出的异常在生成器函数内部没有被捕获,则该异常从 gen.throw()
处流出。prototype
若 gen.throw()
抛出的异常在生成器函数内部被捕获,这次 gen.throw()
调用触发的函数执行过程当中,如有其余未被捕获的异常,也会从 gen.throw()
处流出。code
全部没有在生成器函数内部捕获的异常都会从相应的 gen.next()
或 gen.throw()
调用处流出。发生这种状况时,生成器实例迭代结束。对象
每一个生成器实例都从 Generator.prototype
继承了 return()
方法。
调用 gen.return(val)
可主动终结生成器实例,返回 { value: val, done: true }
。若调用时不提供参数,返回值为 { value: undefined, done: true }
。
next()
、throw()
、return()
的共同点
next()
、throw()
、return()
这三个方法本质上是同一件事,能够放在一块儿理解。它们的做用都是让 Generator 函数恢复执行,而且使用不一样的语句替换yield
表达式。
next()
是将yield
表达式替换成一个值。
const g = function* (x, y) {
let result = yield x + y;
return result;
};
const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}
gen.next(1); // Object {value: 1, done: true}
// 至关于将 let result = yield x + y
// 替换成 let result = 1;
复制代码
上面代码中,第二个
next(1)
方法就至关于将yield
表达式替换成一个值1
。若是next
方法没有参数,就至关于替换成undefined
。
throw()
是将yield
表达式替换成一个throw
语句。
gen.throw(new Error('出错了')); // Uncaught Error: 出错了
// 至关于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));
复制代码
return()是将yield表达式替换成一个return语句。
gen.return(2); // Object {value: 2, done: true}
// 至关于将 let result = yield x + y
// 替换成 let result = return 2;
复制代码
yield*
表达式(yield
委托)语法:
yield* [[expression]]
expression
时返回一个可迭代对象的表达式。
yield*
表达式迭代操做数,并产生它返回的每一个值。
yield*
表达式自己的值是当迭代器关闭时返回的值(即done
为true
时)—— MDN yield*
function* foo() {
yield 'foo1'
yield 'foo2'
}
function* bar() {
yield 'bar1'
yield 'bar2'
return 'bar'
}
function* baz() {
yield 'baz1'
console.log('yield* foo() return:', yield* foo())
console.log('yield* foo() return:', yield* bar())
yield 'baz2'
}
const gen = baz()
for (let v of gen) {
console.log(v)
}
/* >> baz1 foo1 foo2 yield* foo() return: undefined bar1 bar2 yield* foo() return: bar baz2 */
复制代码