宏任务是事件循环的调度单元,每次只执行一个,执行完必须清空全部微任务才进入下一个;它由宿主环境(浏览器或Node.js)发起,来源包括script整体代码、setTimeout/setInterval回调、DOM事件、网络响应及UI渲染等。

宏任务是事件循环的调度单元,每次只取一个执行,执行完后必须清空全部微任务,才能进入下一个宏任务。
宏任务从哪里来
宏任务由宿主环境(浏览器或 Node.js)发起,不是 JS 引擎直接创建的。常见来源包括:
- script 标签整体代码:页面加载时的第一个宏任务
- setTimeout / setInterval 回调:计时器到期后推入宏任务队列
- DOM 事件回调:如 click、scroll、input 等用户交互触发的处理函数
- fetch 或 XMLHttpRequest 的响应回调:网络请求完成时由浏览器插入
- UI 渲染任务:浏览器在微任务清空后、下个宏任务前可能安排一次渲染
宏任务如何被取出执行
事件循环按固定节奏推进,宏任务的调度遵循严格顺序:
- 当前宏任务执行完毕(包括其内部所有同步代码)
- 立即检查并一次性清空整个微任务队列(期间新产生的微任务也会被追加并执行)
- 浏览器可选择进行 UI 渲染(非强制,取决于是否需更新)
- 从宏任务队列头部取出一个**且仅一个**任务,推入调用栈执行
为什么不能跳过或并发执行宏任务
这是单线程模型下的硬性约束:
立即学习“Java免费学习笔记(深入)”;
- JS 主线程同一时刻只能运行一个宏任务,不存在“并行”或“抢占”
- 宏任务之间天然隔离,中间必然插入完整的微任务清空过程
- 即便 setTimeout 设置为 0ms,它也必须排队等待前一个宏任务结束 + 微任务清空之后才执行
调度细节容易忽略的点
实际开发中要注意几个关键事实:
- 宏任务队列是先进先出(FIFO),但不同类型的宏任务可能分属不同队列(如 setTimeout 和 I/O 回调在底层可能分开管理)
- requestAnimationFrame 是特例:它在渲染阶段触发,时机介于微任务清空和下一个宏任务之间,常被归为渲染环节而非标准宏任务
- Node.js 中的 setImmediate 和 process.nextTick 行为与浏览器不同,nextTick 属于微任务,setImmediate 才是宏任务


















