JavaScript宏任务产生后,按先进先出规则排入队列,但必须等待当前执行栈清空且所有微任务执行完毕后才取出执行。常见来源包括setTimeout、setInterval、setImmediate(Node.js)、I/O回调、UI渲染及MessageChannel回调。每次宏任务执行后,会清空整个微任务队列,二者优先级不同。
JavaScript宏任务入队机制详解
作者:技术拾光
时间:2026-07-25
浏览:579
文章目录
先聊聊宏任务的队列规则。在事件循环中,宏任务按照先进先出的顺序排队,但需满足一个前提:当前执行栈必须彻底清空,微任务队列也要全部处理完,宏任务才能被取出执行。常见的宏任务来源包括setTimeout、setInterval、setImmediate(Node.js 专属)、I/O 回调、UI 渲染,以及MessageChannel的onmessage回调。

JavaScript 的宏任务(Macrotask)一旦产生,事件循环就会将其插入对应的队列。等到当前执行栈清空、微任务队列也全部执行完毕,事件循环才会依次取出宏任务并执行。
宏任务的常见来源
以下操作都会创建新的宏任务,并加入队列:
- setTimeout 和 setInterval 的回调函数(即使延时设为 0,也属于宏任务)
- setImmediate(仅在 Node.js 环境中有效)
- I/O 操作完成后的回调(如读取文件、网络请求响应等)
- UI 渲染(浏览器中一次渲染帧可视为一个宏任务的边界)
- MessageChannel 的
port.onmessage回调(注意:虽然经常被用来模拟微任务,但其本质仍是宏任务)
宏任务入队的时机与规则
宏任务并非“立即执行”,而是满足条件后由调度器插入队列:
- 定时器到期时(例如
setTimeout到达指定延迟),回调被加入队列(并非立即执行) - 异步操作结束(如
fetch响应、readFile完成),其then/catch回调中的非 Promise 链部分(即外层回调)属于宏任务 - 宏任务队列采用先进先出(FIFO)结构,但不同来源可能拥有独立子队列(例如 Chrome 中
setTimeout和 I/O 回调可能分属不同队列,但对外表现为统一的调度顺序) - 宏任务只有在当前执行栈为空、且所有微任务都执行完毕后,才会从队列头部取出一个执行
与微任务的关键区别
宏任务和微任务(如 Promise.then、queueMicrotask)共同存在于事件循环中,但优先级截然不同:
- 每次宏任务执行完毕,会清空整个微任务队列(直到没有新的微任务加入)
- 宏任务之间必然穿插一次完整的微任务清空过程
- 微任务不会被新的宏任务“插队”;新的宏任务只能等到当前宏任务以及所有微任务都结束后才能执行
简单验证方式
通过嵌套的 setTimeout 和 Promise 可以观察执行顺序:
console.log('1');setTimeout(() => console.log('2'), 0);Promise.resolve().then(() => console.log('3'));console.log('4');// 输出:1 → 4 → 3 → 2
顺序很清晰:同步代码(1、4)先执行;接着是微任务(3);最后才轮到宏任务队列中的 setTimeout 回调(2)。
实用工具推荐
电子包浆
使用 ›绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
图片压缩
使用 ›选择画质和输出格式,预览压缩前后的大小。