关于generator函数
generator在《ECMAScript 6 入门》中的定义:
Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象(Iterator),也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
简单看看它的使用:
function* gen(n) {
const x = yield n + 5
const y = yield x + 5
return y
}
const it = gen(5)
it.next() // { value: 10, done: false }
it.next(10) // { value: 15, done: false }
it.next(20) // { value: 20, done: true }
it.next() // { value: undefined, done: true }
值得注意的几个地方:
- 调用
generator函数并不会执行其中的代码,而是返回一个遍历器对象
- 调用遍历器对象
next方法generator函数才开始执行,并停在第一个yield的地方,再次调用next,函数恢复执行,停在下一个yield,如此直到函数执行完毕
next方法的返回值是一个对象,该对象value属性是yield后面的内容
next方法的参数,是上下一个yield的返回值,这使得我们可以不停向generator函数中传递数据
- 可以有多个
yield,但只能有一个return
当然,在我们平时使用中,yield后面一般会是Promise
generator函数对我们来说其实并不陌生,async作为generator的语法糖,使用async其实间接的使用了generator,实现generator的自动执行,就是为了帮我们更好的了解async函数的工作方式。在async之前,就已经出现了类似的解决方案,如co模块,它能够实现generator的自动执行,后来将它纳入了标准中,所以,我们要实现的,其实就是co模块。
关于generator的更多细节可以参考阮一峰老师的《ECMAScript 6 入门》
实现
我们的目的在于了解generator函数自动执行的核心逻辑,所以,要实现的是理想状态下的co模块myco,它接收generator函数作为参数
const promise = function(v) {
return new Promise(resolve => {
setTimeout(() => {
resolve(v)
}, 1000)
})
}
const gen = function* () {
const res1 = yield promise(5)
console.log(res1)
const res2 = yield promise(res1 + 5)
console.log(res2)
return (res1 + res2)
}
myco(gen)
第一版:
function myco(gen) {
const it = gen()
const { value, done } = it.next()
value.then(res => {
const { value, done } = it.next(res)
value.then(res => {
const { value, done } = it.next(res)
if (done) {
// 结束
}
})
})
}
myco(gen)
// 1s后 5
// 2s后 10
以上代码中,第一次调用next(),函数gen开始执行,遇到yield暂停执行并返回它后面的promise,该promise在resolve的时候再次调用next(),函数gen恢复执行,遇到第二个yield再次暂停并返回后面的值,如此往复,直到gen函数执行完毕。理解这个过程,我们可以将myco改成递归的方式
注意:next()的参数会成为上一个yield的返回值,这样的好处在于我们可以不停向函数gen中注入值,这种机制使我们可以以同步的方式来表达异步操作,这样更易于理解
第二版:
function myco(gen) {
const it = gen()
function step(v) {
const { value, done } = it.next(v)
if (done) return
value.then(res => {
step(res)
})
}
step()
}
最后,myco函数会返回一个Promise,该Promise的状态会在gen函数执行完毕的时候resolve
最终版:
function myco(gen) {
return new Promise(resolve => {
const it = gen()
function step(v) {
const { value, done } = it.next(v)
if (done) return resolve(value)
value.then(res => {
step(res)
})
}
step()
})
}
myco(gen).then(res => {
console.log(res)
})
// 1s后 5
// 2s后 10 15
关于generator函数
generator在《ECMAScript 6 入门》中的定义:
简单看看它的使用:
值得注意的几个地方:
generator函数并不会执行其中的代码,而是返回一个遍历器对象next方法generator函数才开始执行,并停在第一个yield的地方,再次调用next,函数恢复执行,停在下一个yield,如此直到函数执行完毕next方法的返回值是一个对象,该对象value属性是yield后面的内容next方法的参数,是上下一个yield的返回值,这使得我们可以不停向generator函数中传递数据yield,但只能有一个return当然,在我们平时使用中,
yield后面一般会是Promisegenerator函数对我们来说其实并不陌生,async作为generator的语法糖,使用async其实间接的使用了generator,实现generator的自动执行,就是为了帮我们更好的了解async函数的工作方式。在async之前,就已经出现了类似的解决方案,如co模块,它能够实现generator的自动执行,后来将它纳入了标准中,所以,我们要实现的,其实就是co模块。关于
generator的更多细节可以参考阮一峰老师的《ECMAScript 6 入门》实现
我们的目的在于了解
generator函数自动执行的核心逻辑,所以,要实现的是理想状态下的co模块myco,它接收generator函数作为参数第一版:
以上代码中,第一次调用
next(),函数gen开始执行,遇到yield暂停执行并返回它后面的promise,该promise在resolve的时候再次调用next(),函数gen恢复执行,遇到第二个yield再次暂停并返回后面的值,如此往复,直到gen函数执行完毕。理解这个过程,我们可以将myco改成递归的方式注意:
next()的参数会成为上一个yield的返回值,这样的好处在于我们可以不停向函数gen中注入值,这种机制使我们可以以同步的方式来表达异步操作,这样更易于理解第二版:
最后,
myco函数会返回一个Promise,该Promise的状态会在gen函数执行完毕的时候resolve最终版: