在 JavaScript 的事件循环机制中,宏任务(MacroTask) 和 微任务(MicroTask) 是两种不同优先级的异步任务类型,它们的执行顺序直接影响代码的运行逻辑。以下是它们的核心区别和关键细节:
一、核心区别
特性
宏任务(MacroTask)
微任务(MicroTask)
定义
由宿主环境(如浏览器、Node.js)发起的任务
由 JavaScript 自身发起的任务
执行顺序
在事件循环的每一轮中执行一个宏任务
在当前宏任务执行后、下一个宏任务前全部执行
常见示例
setTimeout, setInterval, I/O 操作, UI 渲染
Promise.then/catch/finally, process.nextTick(Node.js), MutationObserver
优先级
低
高
二、事件循环流程
执行一个宏任务(如主代码块、setTimeout回调)。
执行所有微任务:清空微任务队列。
渲染更新(浏览器环境下可能触发 UI 渲染)。
进入下一轮事件循环,执行下一个宏任务。
三、代码示例分析
console.log("Script start"); // 宏任务(主线程)
setTimeout(() => {
console.log("setTimeout"); // 宏任务(下一轮事件循环)
}, 0);
Promise.resolve()
.then(() => {
console.log("Promise 1"); // 微任务
})
.then(() => {
console.log("Promise 2"); // 微任务
});
console.log("Script end"); // 宏任务(主线程)
输出顺序:
Script start → Script end → Promise 1 → Promise 2 → setTimeout
四、关键细节
微任务优先级更高
微任务会在当前宏任务结束后立即执行,而宏任务需等待下一轮事件循环。
例如:在同一个事件循环中,即使宏任务(如 setTimeout)的回调已就绪,也必须等待所有微任务执行完毕。
微任务会“插队”
如果在微任务中触发新的微任务,这些新微任务会继续加入当前队列,直到队列清空。这可能导致宏任务被长时间阻塞。
应用场景
宏任务:适合不紧急的任务(如延迟操作、批量渲染)。
微任务:适合需要高优先级响应的任务(如 Promise 链式调用、DOM 更新后的回调)。
五、常见问题
为什么 Promise 比 setTimeout 先执行?
Promise 回调属于微任务,会在当前宏任务结束后立即执行。
setTimeout 是宏任务,必须等待所有微任务执行完毕,再进入下一轮事件循环。
如何避免微任务阻塞?
避免在微任务中编写耗时逻辑,否则会延迟后续宏任务执行,导致页面卡顿。
六、总结
宏任务是 JavaScript 异步的“大颗粒”任务,按事件循环的轮次执行。
微任务是“细颗粒”任务,在当前宏任务结束后立即执行,确保高优先级逻辑及时处理。
理解两者的执行顺序,是避免异步代码 Bug 的关键(如竞态条件、渲染顺序问题)。