javascript中宏任务是每次事件循环仅执行一个的独立任务,由宿主环境发起,包括settimeout、setinterval、i/o回调、ui渲染、用户交互事件、requestanimationframe、postmessage及整体代码。

JavaScript 中的宏任务是事件循环中每次只执行一个的独立任务单元,它们由宿主环境(如浏览器或 Node.js)发起,通常涉及较重的操作或外部资源交互。理解哪些操作会生成宏任务,以及它们如何被调度,对预测代码执行顺序至关重要。
常见的宏任务类型
以下是在实际开发中最常遇到的宏任务来源:
-
定时器回调:包括
setTimeout和setInterval。当设定的时间到达后,回调函数被推入宏任务队列,等待下一轮事件循环执行。 -
I/O 操作完成回调:例如
fetch响应返回、XMLHttpRequest完成、Node.js 中的fs.readFile回调等。这些操作本身不阻塞主线程,但其回调属于宏任务。 - UI 渲染(浏览器环境):页面重绘(Repaint)和回流(Reflow)被浏览器作为宏任务处理,通常发生在一批微任务执行完毕之后。
-
用户交互事件回调:如
click、input、keydown等 DOM 事件触发的处理函数,都以宏任务形式进入队列。 -
requestAnimationFrame:虽然它在下一帧渲染前执行,但其回调仍归类为宏任务,且具有较高优先级(紧随微任务之后、其他宏任务之前)。 -
postMessage和MessageChannel消息事件:跨上下文通信产生的回调也是宏任务。 -
脚本初始执行:整个
<script></script>标签内的同步代码,被视为第一个宏任务。
宏任务的触发机制
宏任务不是立即执行的,而是通过宿主环境在特定时机将其注册到宏任务队列中。关键机制如下:
- 宿主环境(如 V8 引擎 + 浏览器/Blink 或 Node.js/libuv)负责监听异步事件完成,并将对应回调“入队”,而非直接执行。
- 只有当前调用栈为空、且本轮微任务队列已清空时,事件循环才会从宏任务队列头部取出一个任务执行。
- 每个宏任务执行完后,引擎会**立刻检查并执行全部待处理的微任务**,再回到宏任务队列取下一个——这意味着微任务总在两个宏任务之间“插队”执行。
- 多个
setTimeout(fn, 0)并不会同时执行,而是按注册顺序依次进入队列,严格遵循 FIFO(先进先出)原则。
容易被误认为微任务的宏任务
有些操作看似轻量,实则属于宏任务,需特别注意:
-
Promise.resolve().then(...)是微任务;但fetch().then(...)的then回调本身是微任务,而fetch请求的发起和底层网络响应处理过程触发的是宏任务。 -
queueMicrotask()明确创建微任务;而setImmediate()(仅 Node.js)和requestIdleCallback()都是宏任务。 - 即使没有显式异步逻辑,DOM 事件监听器绑定后的触发行为,也一定走宏任务流程——这解释了为什么
click后的Promise.then总比同级setTimeout先执行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











