await 不销毁调用栈,而是暂停执行上下文并暂存;待 promise settled 后,引擎在微任务中恢复上下文并从 await 后继续执行,形成控制流让出、上下文挂起、微任务触发恢复的闭环。

await 语句前后执行栈的“断裂”不是栈被销毁,而是函数执行上下文被暂停并暂存;所谓“还原”,是引擎在微任务阶段重新激活该上下文,并从 await 后继续执行。关键不在于栈本身断开,而在于控制流让出、上下文挂起、微任务触发恢复这三步闭环。
await 触发上下文暂停,而非清空调用栈
当 async 函数执行到 await 表达式时:
- JS 引擎立即求值 await 右侧(如 fetch() 或 Promise.resolve()),得到一个 Promise 对象
- 若该 Promise 处于 pending 状态,当前 async 函数的执行上下文(含变量环境、词法环境、await 的位置指针)被保存进内部状态机,但调用栈此时已开始退出——函数暂停,控制权交还给上层宏任务
- 注意:这不是“栈帧被弹出后丢失”,而是上下文被保留、执行流中断;后续代码尚未编译为可执行指令,只是被标记为“待恢复”
微任务注册是上下文还原的触发器
await 并不直接入队,它依赖 Promise 的 settled 状态来注册回调:
- 引擎自动为该 Promise 添加一个 then 回调,这个回调封装了“恢复上下文 + 执行 await 后代码”的逻辑
- 该 then 回调被推入微任务队列,与手写 Promise.then 完全等价,享有相同优先级
- 当前宏任务(包括所有同步代码和其他已排队微任务)结束后,引擎清空微任务队列——此时才真正执行那个恢复回调
上下文还原发生在微任务执行时
当微任务回调运行,引擎做三件事:
- 取出之前保存的 async 函数上下文(变量绑定、await 位置、this 值等)
- 将 await 表达式的值(fulfilled 值或抛出的 rejection)注入当前执行环境
- 从 await 后第一条语句开始继续执行,就像从未中断过——但此时调用栈是全新构建的(新栈帧),只是内容与中断前一致
物理栈的“断裂感”来自执行流切换
看似栈断了,其实是两段同步执行被微任务隔开:
- 第一段:await 前的所有同步代码 → 在当前宏任务中执行,栈逐步压入又退出
- 间隙:宏任务结束 → 渲染(如有)→ 微任务队列清空(此时还原上下文并执行 await 后代码)→ 新的同步片段压栈执行
- 第二段:await 后代码 → 在微任务回调内执行,属于另一个独立的同步块,有自己的调用栈生命周期











