在JavaScript的事件循环机制中,宏任务和微任务分别位于不同的队列。很多人担心宏任务执行时会不会卡住微任务,其实答案是不会。微任务会在每一个宏任务执行完毕之后、下一个宏任务开始前被全部清空,因此宏任务并不会阻塞已经排队或即将排队的微任务。

事件循环的基本规则
浏览器的事件循环每次只取一个宏任务来执行,比如 setTimeout 回调、script 整体代码、UI 渲染等。当这个宏任务运行结束,引擎会立刻检查微任务队列,把里面的任务一次性跑完,包括 Promise 的 then、queueMicrotask 等。只有微任务队列空了,才会取下一个宏任务。
常见宏任务与微任务
- 宏任务:setTimeout、setInterval、I/O、UI 事件
- 微任务:Promise.then、catch、finally、queueMicrotask
用代码看执行顺序
下面这段代码能直观展示宏任务不会阻塞微任务,微任务反而在宏任务之间插队执行:
console.log('开始'); // 宏任务 script 中的同步代码
setTimeout(function() {
console.log('宏任务1'); // 下一个宏任务
Promise.resolve().then(function() {
console.log('宏任务1里的微任务');
});
}, 0);
Promise.resolve().then(function() {
console.log('微任务1'); // 当前宏任务结束后的微任务
});
console.log('结束');
// 输出顺序:
// 开始
// 结束
// 微任务1
// 宏任务1
// 宏任务1里的微任务
为什么感觉像被阻塞
如果在一个宏任务里写了死循环,那整个脚本都会卡住,微任务自然也没机会跑。但这是主线程被同步代码占用,不是宏任务机制阻塞了微任务。只要宏任务正常结束,微任务队列就会被迅速处理。
记住:宏任务之间,微任务永远先清空;宏任务本身不阻塞微任务,只阻塞后续宏任务。
实际开发注意点
| 场景 | 建议 |
|---|---|
| 大量 Promise 链式调用 | 微任务会堆积,但仍在当前宏任务后清空,不会拖到下一宏任务 |
| setTimeout 里套 Promise | Promise 的 then 属于该宏任务产生的微任务,会在该宏任务结束后跑 |
理清这套调度逻辑,就能准确预测异步代码的输出顺序,避免线上逻辑错乱。
JavaScript宏任务微任务修改时间:2026-07-27 09:15:18