宏任务与微任务的核心差异在于调度时机、队列归属及执行规则:宏任务启动事件循环每轮,每次只执行一个;微任务则在每个宏任务结束后立即清空队列,不中断执行。

宏任务和微任务的底层差异,核心不在“谁更快”,而在于它们被调度的时机、所属队列、以及事件循环中不可跳过的执行规则。
调度时机与执行位置不同
宏任务是事件循环每轮“起点”的任务——每次循环只取一个宏任务执行,比如整个脚本、setTimeout 回调、setInterval、I/O 完成回调、UI 渲染。它启动一次完整的执行上下文,结束后才进入下一阶段。
微任务则没有自己的“轮次”,它不参与事件循环的主节奏,而是严格绑定在当前宏任务末尾:只要宏任务执行完、调用栈清空,引擎立刻检查微任务队列,并一次性执行完所有已排队的微任务,中途不插入任何宏任务或渲染。
任务队列归属与清空逻辑不同
浏览器或 Node.js 维护两个独立队列:
立即学习“Java免费学习笔记(深入)”;
- 宏任务队列(Task Queue / Callback Queue):FIFO,每次只取一个,执行后可能产生新宏任务(如 setTimeout 嵌套),但不会立刻执行
- 微任务队列(Microtask Queue):LIFO 或 FIFO(实现细节因引擎而异),但关键行为一致:必须清空——哪怕执行过程中又加入新的微任务(如 Promise.then 内再 new Promise),也会继续执行,直到队列彻底为空
触发来源与宿主环境角色不同
宏任务大多由宿主环境(Host Environment)主动推入:浏览器内核的定时器模块、网络线程、渲染线程完成工作后,把回调放进宏任务队列;Node.js 的 libuv 线程池完成 I/O 后也走这条路。
微任务则更多由JavaScript 引擎自身机制触发:Promise 状态变更时,引擎直接将 then/catch 回调加入微任务队列;MutationObserver 是 DOM 子系统通知 JS 引擎后由引擎安排;process.nextTick 更是 Node.js 引擎在本轮事件循环结束前强制插入的“内部钩子”。
对 UI 渲染的影响不同
浏览器会把 UI 渲染(重排、重绘)当作一个特殊的宏任务,在微任务队列清空之后、下一个宏任务开始之前执行。这意味着:
- 你在微任务里连续修改 DOM,用户看不到中间状态,只会看到最终结果
- 若微任务队列过长(比如链式 Promise 不断 resolve),会阻塞渲染,造成界面卡顿
- 而 setTimeout 回调总在一次渲染之后才执行,天然自带“帧间隔”,更适合动画或节流场景



















