生成器
generator function
function* genFunc() {
console.log('hello')
yield 'a'
}
funciton 关键字后面有一个 *,它的前后可以有空格,怎么写由代码规范规定。ESLint rule generator-star-spacing。
generator function 不能用作构造器,即不可以使用 new 操作符。
调用 generator function,不会执行函数体,返回一个 generator 对象。generator 同时实现了 iterable protocol 和 the iterator protocol。
- generator 是 iterable,因为它有 Symbol.iterator 方法
- generator 也是 iterator,因为它有 next 方法。
- generator 的 iterator 是它自己。
var genObj = genFunc()
typeof genObj[Symbol.iterator] // 'function'
typeof genObj.next // 'function'
genObj[Symbol.iterator]() === genObj // true
generator 有三个方法:next(), return(), throw()。
next()
generator 每次调用 next(),generator function 运行到下一个 yield 暂停。
yeild 是一元操作符,只能用在 generator function 内(contextual keyword)。
.next(arg) => { value, done }
- 一个可选参数,这个参数将作为当前暂停的
yield表达式的值; - 返回一个对象
value是下一个yield的操作数。done,布尔值,表示是否迭代是否接受
function* genFunc() { // A
const input = yield 'hi' // B
console.log(input)
return 'bye'
}
const genObj = genFunc()
genObj.next('a') // 第一次调用
genObj.next('b') // 第二次调用
genObj.next('c') // 第三次调用
// Output:
// b
第一次调用 next()
- 暂停在 A 处,此处没有
yield,于是忽略next()的参数。 - 运行到 B 处遇到
yield,暂停,返回{value: 'hi', done: false},其中value值为这个yield的操作数(没有则视为undefined)。
第二次调用 next()
- 暂停在 B 处,此处有
yield,这个yield表达式的值为next()的参数(没有则视为undefined)。于是'b'赋值给input。 - 继续运行遇到
return, 返回{value: 'bye', done: true},其中value值为返回值(没有则视为undefined),done: true表示迭代结束。
第三次调用 next(),迭代已经结束,返回 {value: undefined, done: true}。后面再调用 next(),同样如此。
可以看到 next()
- 第一次调用因为没有 yield,参数被忽略,它的作用只是启动 generator。
- 是不对称的,参数给当前 yield,而返回的是下一个 yield 的操作数。
yield*
yield* iterable,将流程转到 iterable, 这个表达式的值是 iterable done value。
yield 后面的 *,它的前后可以有空格,怎么写由代码规范规定。ESLint yield-star-spacing。
function* g1() {
yield 'abc'
}
function* g2() {
yield* 'abc'
}
console.log([...g1()]) // ['abc']
console.log([...g2()]) // ['a', 'b', 'c']
.return()
return(x) 在当前暂停的位置终止 generator(在当前 yield 处 return x),返回 {value: x, done: true}。
function* genFunc() { // A
yield 'hi' // B
console.log('terminated')
yield 'bye' // C
}
const genObj = genFunc()
console.log(genObj.next()) // 启动 generator, 运行到 B 处暂停
console.log(genObj.return(1)) // 在 B 处 return
// {value: 1, done: true}
现在 generator 已完成,继续调用会如何?
console.log(genObj.next('next'))
// {value: undefined, done: true}
console.log(genObj.return('return'))
// {value: "return", done: true}
不启动 generator 也可以使用
const genObj2 = genFunc()
console.log(genObj2.return(2)) // 在 A 处 return
// {value: 2, done: true}
finally 会暂停 try 内的 return,因此 finally 可以阻止 return() 终止 generator。
function* genFunc() { // A
try {
yield 'hi' // B
} finally {
yield 'haha' // C
}
}
const genObj = genFunc()
console.log(genObj.next()) // 启动 generator, 运行到 B 处暂停
console.log(genObj.return('result')) // 在 B 处 return,进入 finally, 到 C 处暂停
// {value: "haha", done: false}
console.log(genObj.next()) //
// {value: "result", done: done}
.throw()
throw() 在当前暂停的位置抛出异常。
function* genFunc() { // A
try {
yield // B
} catch (error) {
console.log('Caught: ' + error)
}
}
const genObj = genFunc()
console.log(genObj.next()) // 启动 generator, 运行到 B 处暂停
console.log(genObj.throw(new Error('Problem!'))) // 在 B 处抛出异常
// {value: undefined, done: true}
不启动 generator 也可以使用
const genObj2 = genFunc()
console.log(genObj2.throw(new Error('Problem!'))) // 在 A 处抛出异常
// Uncaught Error: Problem!