async函数执行上下文与普通函数一致,但通过微任务调度实现挂起与恢复,不销毁原上下文;await不创建新上下文,仅暂停并注册微任务,恢复时复用同一FEC,保持词法环境、this和闭包一致性。

异步函数(async 函数)的执行上下文和普通函数基本一致,但它在创建、挂起与恢复过程中引入了微任务调度机制,导致执行上下文的生命周期被拆分,并与 Promise 链深度耦合。
async 函数会创建独立的函数执行上下文
每次调用 async 函数时,JavaScript 引擎仍会为其创建一个全新的函数执行上下文(FEC),该上下文包含:参数、this 绑定、词法环境、变量环境,以及一条指向外层作用域的作用域链。
关键点在于:这个 FEC 并不会一次性执行到底——遇到 await 表达式时,函数暂停执行,当前 FEC 被“挂起”,控制权交还给调用者,但上下文并未销毁。
- 挂起时,FEC 保留在内存中(尤其当内部有闭包引用外部变量时)
- await 后续的代码会被包装成微任务(Promise.then 回调),等当前宏任务执行完、微任务队列清空前再恢复执行
- 恢复时,引擎会重新激活同一个 FEC(不是新建),继续从 await 下一行开始执行
await 不会创建新执行上下文,但会触发微任务调度
await 本身不是函数调用,不产生新的 FEC。它的行为是:
立即学习“Java免费学习笔记(深入)”;
- 先对右侧表达式求值;若结果是 thenable(如 Promise),则调用其
then方法 - 将 await 后面的剩余逻辑注册为
then的回调,放入微任务队列 - 当前 FEC 暂停,执行栈弹出(但 FEC 对象未销毁)
例如:
console.log('start');
await Promise.resolve();
console.log('end'); // 这行在微任务中执行
}
执行流程:
→ 全局 EC → foo FEC 入栈 → 打印 'start' → await 触发 → foo FEC 挂起、出栈 → 微任务入队 → 当前宏任务结束 → 微任务执行 → foo FEC **恢复** → 打印 'end'
执行上下文与 Promise 链的绑定关系
async 函数返回一个 Promise,其 resolve 值由函数体中 return 或最终执行完毕的值决定。这个 Promise 的状态变化,与函数 FEC 的生命周期间接关联:
- FEC 创建 → Promise 初始化为 pending
- FEC 正常完成(无未捕获异常)→ Promise resolve
- FEC 抛出未捕获错误 → Promise reject
- await 后的代码若访问了上层变量,依赖的是原 FEC 的词法环境,不是新上下文
这意味着:即使 async 函数被多次调用,每个调用都拥有自己独立的 FEC 和闭包环境,彼此变量不干扰。
注意 this 和 arguments 的延续性
async 函数中,this 在创建阶段就已绑定,await 挂起/恢复过程不会改变它;arguments 对象也只在 FEC 创建阶段生成一次,后续 await 后仍可访问(除非用箭头函数替代)。
对比普通函数,区别仅在于执行被中断并延后,而非上下文重建。所以:
- 不能把 await 理解为“跳出当前函数”
- 也不能认为 await 后的代码运行在另一个上下文中
- 所有 var/let/const 声明、闭包引用、this 指向,都保持原 FEC 的上下文一致性


















