JavaScript 宏任务产生后如何排入队列

作者:袖梨 2026-07-16
宏任务在事件循环中按FIFO顺序执行,需等待执行栈清空及微任务队列清空后才被取出;常见来源包括setTimeout、setInterval、setImmediate(Node.js)、I/O回调、UI渲染和MessageChannel的onmessage回调。

JavaScript 的宏任务(Macrotask)在产生后,会根据事件循环机制被排入对应的宏任务队列,等待当前执行栈清空、微任务队列执行完毕后,由事件循环依次取出执行。

宏任务的常见来源

以下操作会创建新的宏任务,并将其加入宏任务队列:

  • setTimeoutsetInterval 的回调函数(即使延时为 0,也属于宏任务)
  • setImmediate(仅 Node.js 环境支持)
  • I/O 操作完成后的回调(如文件读取、网络请求响应等)
  • UI 渲染(浏览器中一次渲染帧可视为一个宏任务边界)
  • MessageChannelport.onmessage 回调(注意:虽然它常被用来模拟微任务,但实际是宏任务)

宏任务入队的时机与规则

宏任务不是“立刻执行”,而是在满足条件时被调度器插入宏任务队列:

  • 定时器到期时(如 setTimeout 到达设定延迟),回调被加入宏任务队列(不是立即执行)
  • 异步操作(如 fetch 响应、readFile 完成)结束后,其 then/catch 回调中的 非 Promise 链部分(即外层回调)属于宏任务
  • 宏任务队列是先进先出(FIFO)结构,但不同来源可能有独立子队列(如 Chrome 中 setTimeout 和 I/O 回调可能分属不同队列,但对外表现为统一调度顺序)
  • 宏任务只有在当前执行栈为空、且所有微任务执行完后,才会从队列头部取出一个执行

和微任务的关键区别

宏任务与微任务(如 Promise.then、queueMicrotask)共存于事件循环,但优先级不同:

立即学习“Java免费学习笔记(深入)”;

  • 每次宏任务执行完毕后,会清空整个微任务队列(直到无新微任务加入)
  • 宏任务之间必然穿插一次完整的微任务清空过程
  • 微任务不会被新的宏任务“插队”;而新的宏任务只能等到当前宏任务 + 所有微任务都结束后才执行

简单验证方式

可通过嵌套 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)。

相关文章

精彩推荐