async/await是基于generator+promise的语法糖,手写执行器runasync可将generator函数转译为等效async行为:通过递归处理yield出的promise,保持上下文、错误冒泡及同步穿透语义,实现与原生一致的异步控制流。

async/await 本身不是原生运行机制,而是 JavaScript 引擎基于 Generator + Promise 的语法糖。所谓“手写编译器模拟器”,本质是把 async 函数转译为可执行的 Generator 函数,并用自动执行器驱动它——不依赖 runtime,纯逻辑复刻。
Generator 是 async/await 的骨架
async 函数被 Babel 或 TypeScript 编译后,会变成一个返回 Generator 的包装函数(通常带 __awaiter 辅助逻辑)。关键在于:Generator 提供了暂停/恢复能力,而 Promise 提供了异步等待能力,二者结合就能模拟 await 的“阻塞感”。
- 每次
yield对应一个await表达式,产出的是 Promise 实例 - 执行器需监听该 Promise 的 resolve/reject,并用
next(value)或throw(error)恢复 Generator - Generator 内部不能直接 return Promise,但执行器可以统一捕获
done: true时的value作为最终结果
手写执行器:runAsync 就是核心胶水
你不需要重写 V8,只需实现一个通用执行器,它接收 Generator 函数,启动并递归处理 yield 出来的 Promise:
- 调用
gen = generatorFn()得到迭代器 - 定义
function step(nextFn) { const { value, done } = nextFn(); if (done) return Promise.resolve(value); return Promise.resolve(value).then(res => step(() => gen.next(res))).catch(err => step(() => gen.throw(err))); } - 首调
step(() => gen.next())即启动整个链
处理 await 的语义细节
真实 async/await 有几处易忽略但必须复刻的行为:
-
上下文保持:Generator 内部
this和词法作用域必须与原始 async 函数一致(所以要用generatorFn.bind(this)启动) - 错误冒泡:Promise reject 不仅要 throw 给 Generator,还要确保未捕获时能透传到外层 Promise 链终点
- return/throw 同步穿透:Generator 中同步 return 或 throw 应立即结束,不走 Promise 流程;只有 yield 后接 Promise 才触发异步分支
兼容性补丁:让 Generator 支持 await-like 语法
若想在旧环境手动写类似 async 的函数,可封装一层语法友好的工厂:
<font size="2">const asyncify = fn => function(...args) {<br> const gen = fn.apply(this, args);<br> return runAsync(gen);<br>};<br><br>const delay = ms => new Promise(r => setTimeout(r, ms));<br><br>const fetchUser = asyncify(function* () {<br> yield delay(100);<br> return { id: 1, name: 'Alice' };<br>});</font>
这样调用 fetchUser().then(...) 的行为,就和 async function fetchUser() { await delay(100); return ... } 完全一致。











