宏任务是事件循环中每次迭代执行一个的任务,优先级低于微任务,常见类型包括setTimeout、setInterval、setImmediate(Node.js)、I/O回调、UI渲染、postMessage、MessageChannel及script初始执行;其执行顺序为:执行一个宏任务→清空所有微任务→渲染(浏览器)→进入下一宏任务。

JavaScript 中的宏任务(Macrotask)是事件循环中的一类任务,它们在每次事件循环的迭代中执行一个,且优先级低于微任务(Microtask)。宏任务的主要特点是:每个宏任务执行完后,会清空当前所有微任务队列,再进入下一个宏任务。
常见的宏任务有哪些
以下是最典型的宏任务来源,它们都会被推入宏任务队列(macrotask queue),等待主线程空闲时依次执行:
- setTimeout 和 setInterval 的回调函数
- setImmediate(仅 Node.js 环境支持)
- I/O 操作完成后的回调(如文件读取、网络请求的 callback,Node.js 中)
- UI 渲染任务(浏览器环境,虽不可直接调用,但属于宏任务范畴)
- postMessage 和 MessageChannel 的消息处理回调
- script 标签的初始执行(整个脚本本身就是一个宏任务)
宏任务和微任务的执行顺序
理解宏任务的关键在于它和微任务的协作关系。一次事件循环包含:
- 执行一个宏任务(如 setTimeout 回调)
- 立即执行所有已排队的微任务(如 Promise.then、queueMicrotask)
- 渲染(浏览器环境,若需要)
- 进入下一个宏任务
例如:
立即学习“Java免费学习笔记(深入)”;
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 输出顺序:1 → 4 → 3 → 2
这里 setTimeout 是宏任务,排在下一轮;而 Promise.then 是微任务,插在当前宏任务末尾、下个宏任务前执行。
如何手动触发一个宏任务
没有直接 API 叫 “createMacrotask”,但你可以用标准方式“间接创建”:
- 使用 setTimeout(fn, 0) 或 setTimeout(fn, 1)(最常用)
- 使用 setImmediate(fn)(Node.js)
- 用 MessageChannel 发送空消息(轻量、无延迟,但仍是宏任务):
const channel = new MessageChannel();
channel.port2.onmessage = () => console.log('宏任务执行了');
channel.port1.postMessage(0);
注意:虽然 MessageChannel 延迟极低,但它仍属于宏任务,且会在当前微任务之后、下一个宏任务阶段执行。
为什么 setTimeout(0) 不是立刻执行
因为 JavaScript 的事件循环机制规定:即使延迟设为 0,setTimeout 回调也必须等到当前宏任务 + 所有微任务执行完毕后,才进入下一轮宏任务队列。它只是“尽快安排”,不是“立即插入当前执行栈”。
这也解释了为什么它比 Promise.then 慢——后者属于微任务,抢占更靠前的时机。
基本上就这些。宏任务是理解 JS 异步行为的基石,掌握它能帮你理清异步代码的真实执行节奏。











