async/await本质是基于Promise的微任务调度,不创建新线程,await将后续逻辑注册为Promise的then回调并入微任务队列,宏任务结束后立即清空执行。

async/await 的异步执行本质是基于 Promise 的微任务调度,它不创建新线程,也不改变 JavaScript 单线程事件循环结构,而是将 await 后的代码自动封装进微任务队列,在当前宏任务结束后立即执行。
await 不是“暂停”,而是“让出控制权”
async 函数内部从上到下同步执行,直到遇到 await。此时 JS 引擎会:
- 立即执行 await 右侧的表达式(比如 Promise.resolve() 或 fetch())
- 若该表达式返回一个 pending Promise,则函数暂停,并把 await 后面的剩余逻辑注册为该 Promise 的 then 回调
- 这个 then 回调会被加入微任务队列,不是立即运行,而是等当前宏任务(包括所有同步代码和已排队的微任务)执行完后才触发
微任务队列执行时机很关键
每次宏任务结束时,JS 引擎会清空整个微任务队列(不是只取一个),按先进先出顺序执行所有已排队的微任务。这意味着:
- 多个 await、Promise.then、queueMicrotask() 注册的任务,会在同一轮微任务阶段全部执行完毕
- 哪怕中间插入了 setTimeout(fn, 0),它也得等到这一轮微任务全跑完才进入下一轮宏任务
- 浏览器中 Promise.then 和 queueMicrotask 优先级相同;Node 中 process.nextTick 会比 Promise.then 更早执行
常见误区:setTimeout(fn, 0) ≠ 立即执行
很多人误以为 setTimeout(fn, 0) 能插队到 await 后面,其实它只是被放入宏任务队列末尾。执行顺序始终是:
- 同步代码 → 微任务(await 后、Promise.then 等)→ 渲染(可选)→ 下一个宏任务(如 setTimeout 回调)
- 例如:console.log('A'); setTimeout(() => console.log('B'), 0); async function f() { console.log('C'); await Promise.resolve(); console.log('D'); } f(); console.log('E'); 输出顺序是 A → C → E → D → B
如何主动控制微任务时机
除了 await 和 Promise.then,还可以用原生 API 显式插入微任务:
- queueMicrotask(callback):标准方法,语义清晰,兼容现代环境
- MutationObserver:适合需要监听 DOM 变化并响应的场景,回调也是微任务
- 避免滥用 process.nextTick:仅限 Node.js,且优先级高于 Promise.then,可能打乱预期顺序


















