异步任务执行顺序遵循同步→微任务→宏任务层级,每轮宏任务后清空全部微任务再取下一宏任务;Promise.then等属微任务,setTimeout等属宏任务;setTimeout(fn,0)仍需等待当前宏任务及所有微任务完成后才执行。

预测异步任务执行顺序,关键在于理清三类任务的层级关系:同步代码 → 微任务 → 宏任务,且每轮宏任务执行完后,必须清空当轮产生的所有微任务,再取下一个宏任务。
看清楚任务类型归属
不是所有“看起来异步”的代码都归同一队列。区分标准不看写法先后,而看回调注册机制:
-
同步任务:直接在主线程执行,如
console.log、变量赋值、函数调用(非回调部分) -
微任务:Promise 的
.then/.catch/.finally、queueMicrotask()、MutationObserver回调 -
宏任务:
setTimeout、setInterval、I/O 回调、script 标签整体、UI 渲染(浏览器环境)
记住事件循环的固定节奏
每次循环只推进一个“宏观步骤”,但微任务会批量清空:
- 执行一个宏任务(比如全局脚本或某个
setTimeout回调) - 该宏任务执行过程中,新产生的微任务立即入微任务队列
- 宏任务结束后,**立刻、连续、无中断地执行全部现存微任务**(包括本轮新加入的)
- 微任务队列变空后,才从宏任务队列取下一个任务,开始下一轮
注意嵌套和延迟带来的错觉
setTimeout(fn, 0) 并不等于“马上执行”,它只是尽快进入宏任务队列——仍要等当前宏任务+所有微任务走完才轮到它:
- 哪怕
setTimeout写在Promise.then前面,只要两者都在同一宏任务中注册,Promise.then一定先跑 - 在微任务里再注册微任务(如
Promise.resolve().then(() => Promise.resolve().then(...))),它们会按链式顺序在本轮微任务阶段依次执行 - 在微任务里注册宏任务(如
Promise.then(() => setTimeout(...))),该setTimeout要等到下一轮宏任务才执行
动手验证比死记更可靠
遇到复杂场景,用最小可复现代码快速测试:
- 把所有
console.log加上明确标记,如'sync-1'、'micro-2'、'macro-3' - 避免依赖
setTimeout的具体延迟值,统一用0减少干扰 - 逐行注释掉部分逻辑,观察输出变化,反推执行路径

















