宏任务和微任务执行规则是“一宏+全微”:每个宏任务执行完后立即清空全部微任务队列,再取下一个宏任务;同步代码→宏任务内执行,promise.then等加入微任务队列,settimeout等加入宏任务队列。

宏任务和微任务不是“交替执行”,而是每个宏任务执行完后,立即清空全部微任务队列,再取下一个宏任务。这是事件循环最核心的规则,不是轮换,而是“一宏 + 全微”的固定节奏。
执行流程:一次循环只跑一个宏任务
浏览器或 Node.js 的事件循环每次只从宏任务队列中取出一个任务执行(比如 setTimeout 回调、页面初始 script、用户点击事件等)。这个任务本身可能包含同步代码、新的宏任务注册、以及微任务注册。
关键点在于:该宏任务执行完毕后,JS 引擎不马上取下一个宏任务,而是转向微任务队列——只要队列非空,就持续取出并执行,直到清空为止。
- 同步代码 → 当前宏任务的一部分
- 遇到 Promise.then / queueMicrotask → 加入微任务队列(暂不执行)
- 遇到 setTimeout → 加入宏任务队列(等待下一轮)
- 当前宏任务结束 → 立即执行所有已排队的微任务(包括过程中新加入的)
- 微任务队列清空 → 浏览器可能触发 UI 渲染(仅浏览器环境)→ 取下一个宏任务
哪些是宏任务?哪些是微任务?
区分依据不是“异步与否”,而是由谁调度、何时插入队列:
-
宏任务:由宿主环境(浏览器/Node)发起,每次事件循环只处理一个。
常见:script 整体、setTimeout、setInterval、I/O 回调、UI 渲染、postMessage、用户交互事件(click、input) -
微任务:由 JS 引擎自身调度,优先级更高,必须在当前宏任务结束后立刻执行。
常见:Promise.then/catch/finally、queueMicrotask、MutationObserver 回调
注意:process.nextTick(仅 Node)优先级比 Promise 还高,但它属于 Node 特有机制,不属于标准微任务。
典型执行顺序分析(带嵌套)
看这段代码:
console.log('A');setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => {
console.log('C');
Promise.resolve().then(() => console.log('D'));
});
console.log('E');
输出顺序是:A → E → C → D → B
原因:
- A 和 E 是同步代码,立即执行
- setTimeout 回调进入宏任务队列(等待下一轮)
- Promise.then 进入微任务队列;其内部又产生一个微任务(D),也加入队尾
- 当前宏任务结束 → 执行 C → 执行 D(微任务队列被清空)
- 下一轮事件循环 → 执行 B
容易误解的点
很多人以为“微任务执行完一个,就回去执行一个宏任务”,其实不是。微任务队列具有递归清空特性:只要执行过程中新产生了微任务(比如在 then 里再写一个 then),它会立即被追加到当前队列末尾,并继续执行,直到彻底为空。
另外,多个 setTimeout 的执行顺序取决于它们的延迟时间到期先后,而不是代码书写顺序。例如 setTimeout(fn1, 0) 和 setTimeout(fn2, 3),即使 fn1 写在后面,只要 0ms 先到期,fn1 就先进宏任务队列,先执行。











