
await 并非等待 Promise 完成才继续,而是强制将后续代码推入微任务队列,确保异步恢复的可预测性——无论 Promise 是立即 resolve 还是延迟 resolve,await 后的语句总在当前同步任务结束后、下一个微任务阶段执行。
`await` 并非等待 promise 完成才继续,而是强制将后续代码推入微任务队列,确保异步恢复的可预测性——无论 promise 是立即 resolve 还是延迟 resolve,`await` 后的语句总在当前同步任务结束后、下一个微任务阶段执行。
你提供的这段代码,看似“全是同步操作”,却输出了出人意料的顺序:
async function f1() {
console.log('this is f1');
let res = await f3();
console.log('result of f1 from f3 = ', res['status']);
console.log('end of f1');
}
async function f3() {
console.log('this is f3');
console.log('returning res from f3');
return {"status": 600};
}
f1();
console.log('inside global context');
实际输出(关键顺序):
this is f1 this is f3 returning res from f3 inside global context result of f1 from f3 = 600 end of f1
初学者常误以为:“f3() 没有真正异步操作,返回的是一个立刻 resolve 的 Promise,那 await f3() 应该立刻继续执行 f1 剩余逻辑”。但事实是:await 从不“跳过”异步调度 —— 它总是让出控制权,把后续代码放入微任务队列(microtask queue)。
? 为什么 inside global context 先于 result of f1... 打印?
因为 await 的本质不是“条件等待”,而是一种统一的异步恢复机制:
-
f3()是async函数 → 自动返回Promise.resolve({"status": 600}); -
await f3()触发后,JavaScript 引擎会:- 立即获取
f3()返回的 Promise; -
暂停
f1的执行上下文(保存状态,退出调用栈); - 将
f1中await后的剩余逻辑(即两个console.log)注册为一个微任务; - 继续执行当前同步任务栈中剩余代码 → 此时
f1()已暂停,控制权回到全局,执行console.log('inside global context'); - 当前同步脚本执行完毕后,JS 引擎立即清空微任务队列 → 执行那个被挂起的
f1后续逻辑。
- 立即获取
✅ 这就是为什么 inside global context 总是插在中间 —— 它属于“同步主线程任务”,而 await 后的代码属于“微任务”,严格遵循 同步任务 → 微任务 → 宏任务(如 setTimeout) 的事件循环优先级。
? 关键结论(务必牢记)
| 概念 | 说明 |
|---|---|
async 函数必返回 Promise |
即使 return 42 或 return {a:1},也会被自动包装为 Promise.resolve(...)。f3() 返回的不是普通对象,而是已 resolve 的 Promise。 |
await 总是异步恢复 |
不管 Promise 是否已 settle,await 后的代码永不在当前同步执行流中运行 —— 它一定会被推迟到下一个微任务阶段。这是设计使然,保障行为一致性。 |
| 微任务 ≠ 延迟 | 微任务执行极快(无渲染、无 I/O、无 timer 中断),但它仍属于“异步阶段”。它保证了:await Promise.resolve() 和 await fetch(...) 在恢复时机上具有相同语义,避免“竞态式 bug”(如变量未定义、状态不一致)。 |
? 验证:对比 await 与 .then() 的等价性
以下两段代码行为完全一致:
// 方式一:await(推荐,语义清晰)
async function demo1() {
console.log('start');
await Promise.resolve();
console.log('after await');
}
demo1();
console.log('global');
// 方式二:.then(底层等价)
function demo2() {
console.log('start');
return Promise.resolve().then(() => {
console.log('after then');
});
}
demo2();
console.log('global');
输出均为:
start global after await / after then
这印证了:await x 在语义上 ≈ x.then(res => { /* 后续逻辑 */ }),且该 then 回调一定在微任务中执行。
⚠️ 常见误区提醒
❌ 错误认知:“
await只在 Promise pending 时才‘等待’”
✅ 正确认知:“await总是暂停 + 微任务调度”,与 Promise 状态无关。-
❌ 错误写法:在
async函数中漏掉await调用另一个async函数async function bad() { f3(); // ❌ 忘了 await!f3() 返回 Promise 但未等待 → 后续逻辑立即执行 console.log('I run too early!'); } ✅ 正确实践:始终
await异步函数调用,或显式.then()/catch()处理,避免“幽灵 Promise”。
✅ 总结:await 是可控的“语法级断点”
它不阻塞线程,也不依赖真实耗时;它是一个确定性的调度指令:
“请暂停我,把接下来的代码放进微任务队列,等本轮同步代码跑完再执行。”
理解这一点,你就跨过了 async/await 最大的认知门槛——从此不再困惑“为什么明明没异步,却像异步一样延迟”,而是能自信设计可预测、易调试、高性能的异步流程。










