async/await编译为ES5后本质是状态机模拟:每个await对应一个switch case跳转点,通过context.next控制流程,用Promise.then触发状态迁移,而非真正挂起函数。

async/await 编译为 ES5 后,核心是用 状态机 + switch/case + 闭包保存上下文 模拟异步流程,而非真正“挂起”函数。Babel 等工具(如 `@babel/preset-env`)在目标为 ES5 时,会把 async 函数转为基于 regeneratorRuntime 的 generator 封装函数,并进一步展开为带状态变量的普通函数,其中关键控制逻辑就落在 switch (context.prev = context.next) 这类结构上。
状态机如何对应 await 的暂停与恢复
每个 await 表达式被编译为一个状态跳转点。函数体内所有语句按执行顺序编号(0, 1, 2…),context.next 记录当前应执行哪一步;await 后的 Promise resolve 时,调用 context.next = nextLabel 并触发 switch 跳转到后续状态。
- 初始进入函数时,
context.prev = 0,context.next = 0,执行 case 0 - 遇到
await promise,不直接等待,而是:
– 把后续代码打包成onFulfilled回调
– 设置context.next = 1(假设下一个是状态 1)
– 返回并退出当前同步段 - Promise 完成后,调用
context.stop()或直接跳转到case 1,继续执行
手动写一个简易 switch/case 状态机模拟 async/await
不依赖 regenerator,可手写等效逻辑:用 state 变量记录进度,用 switch 分发执行,用 Promise.then 触发状态迁移。
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
// 原始 async 函数
async function example() {
console.log('start');
await sleep(100);
console.log('middle');
await sleep(100);
console.log('end');
}
// 手动状态机等价实现(ES5 兼容)
function exampleStateMachine() {
var state = 0;
var context = { value: undefined, done: false };
function step() {
switch (state) {
case 0:
console.log('start');
state = 1;
return sleep(100).then(step);
case 1:
console.log('middle');
state = 2;
return sleep(100).then(step);
case 2:
console.log('end');
context.done = true;
return Promise.resolve();
default:
context.done = true;
return Promise.resolve();
}
}
return step();
}
真实 Babel 输出的关键特征
Babel 输出不是纯 switch/case,而是结合了 try/catch、context 对象(含 prev/next/sent/done)、continue 跳转和 context.stop() 终止。但主干确实是:
- 一个大
while (1)或递归step()驱动 - 每个
case对应一个 await 分界点或语句块 -
context.next在 Promise then 中被显式赋值,决定下次进哪个 case - yield/return 被转为
context.delegate = { next: ... }或直接context.abrupt(...)
注意事项与限制
这种模拟无法做到真正的栈暂停,所以:
- 错误堆栈不连贯(原始行号丢失,需 source map)
- 无法在 debugger 中“单步跨过 await”,只能在 then 回调里断点
- 闭包必须捕获所有中间变量(如
let x = 1; await f(); console.log(x);中x会被提升为 context 属性) - 循环中的 await(如 for-await)会生成嵌套状态,case 数量显著增加
本质上,这是用同步语言设施(switch + Promise 回调)对异步控制流做的线性建模——不复杂但容易忽略其运行时开销和调试差异。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











