手写 Generator 自动执行器,说明它和 async/await 的关系。
先理解原理和示例,再练习完整口述与追问。
回答要点
执行器调用 Generator 得到迭代器,每次 next(value) 拿到 yield 出的值,用 Promise.resolve 包装后等待:成功则把结果 next 回去,失败则 throw 回 Generator,使其内部 try...catch 能捕获;done 为 true 时以返回值 resolve。async/await 可以理解为语言内置的同一套机制。
原理拆解
yield 暂停函数并把值交给外部,外部再决定何时、用什么值恢复。把“等待 Promise 完成后恢复”的逻辑写成递归的 step 函数,就得到了 co 库的核心。同步调用 it.next() 或 it.throw() 本身可能抛错(Generator 内部未捕获的异常),所以要用 try...catch 包住并 reject。
async 函数在规范中的行为与此几乎一致:await 把值转换为 Promise,在其完成后恢复执行,拒绝时在 await 处抛出。区别是 async/await 由引擎原生实现,有更好的调用栈和性能,返回值必然是 Promise。
示例与推演
function run(generatorFn, ...args) {
return new Promise((resolve, reject) => {
const it = generatorFn.apply(this, args)
function step(method, value) {
let result
try {
result = it[method](value)
} catch (error) {
reject(error)
return
}
if (result.done) {
resolve(result.value)
return
}
Promise.resolve(result.value).then(
resolved => step('next', resolved),
error => step('throw', error)
)
}
step('next')
})
}
推演:run(function* () { const a = yield Promise.resolve(1); const b = yield a + 1; return a + b }) 中,第一次 yield 出 Promise,完成后 next(1) 使 a 为 1;第二次 yield 出普通值 2,Promise.resolve(2) 后 next(2);最后返回 3,外部 Promise resolve 为 3。若某个 yield 的 Promise 拒绝,错误通过 it.throw 进入 Generator,没有被捕获时由 catch 分支 reject。
易错点
只处理成功分支,拒绝时 Promise 链断开,外部永远等待;直接调用 result.value.then 不能兼容 yield 普通值的情况。
参考回答可直接练习口述
执行器先调用 Generator 拿到迭代器,写一个 step 函数:调用 next 或 throw 拿到结果,done 了就 resolve 返回值;否则用 Promise.resolve 包装 yield 出来的值,成功时把结果通过 next 传回去,失败时用 throw 抛回 Generator,让内部 try...catch 能处理,同步抛错就直接 reject。async/await 就是引擎内置的这套机制,await 相当于 yield 一个 Promise 并由执行器自动恢复。