微任务一定在当前宏任务结束后、下一个宏任务开始前执行,且会清空整个微任务队列;宏任务由宿主环境发起(如settimeout、script、dom事件),微任务由js引擎发起(如promise.then、queuemicrotask、mutationobserver)。

微任务一定在当前宏任务结束后、下一个宏任务开始前执行,这是最核心的优先级规则。宏任务之间互不插队,而微任务可以“插队”进两个宏任务之间,且会清空整个微任务队列才进入下一宏任务。
看任务来源:哪些是宏任务,哪些是微任务
宏任务由宿主环境(浏览器或 Node.js)提供,粒度较粗:
- script 整体代码(页面加载时执行的全部同步脚本)
- setTimeout / setInterval 的回调函数
- UI 渲染(浏览器自动触发,属于宏任务周期的一部分)
- postMessage、I/O 回调、DOM 事件监听器(如 click、input)
- requestAnimationFrame(虽与渲染强相关,但按宏任务调度)
微任务由 JS 引擎内部机制触发,粒度更细、响应更及时:
- Promise.then/catch/finally 的回调函数
- MutationObserver 的回调
- queueMicrotask() 显式加入的微任务
- async/await 中 await 后的代码(本质是 Promise.then)
注意:process.nextTick(仅 Node.js)优先级比所有微任务还高,但它不属于标准微任务,是 Node 特有的高优队列。
看执行时机:一个宏任务结束,立刻执行全部微任务
事件循环每轮只取一个宏任务执行;该宏任务运行完后,不直接取下一个宏任务,而是先检查并一次性执行完所有待处理的微任务,直到微任务队列为空。
例如:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
输出顺序是 1 → 4 → 3 → 2。因为:
- 1 和 4 是同步代码,立即执行
- setTimeout 回调进宏任务队列
- Promise.then 回调进微任务队列
- 同步代码执行完,立刻执行微任务队列 → 输出 3
- 微任务清空后,才从宏任务队列取 setTimeout → 输出 2
看嵌套行为:微任务可产生新微任务,仍属本轮清空
在微任务中调用 Promise.resolve().then() 或 queueMicrotask(),新生成的微任务不会等到下一轮事件循环,而是加入当前微任务队列末尾,继续执行,直到队列彻底为空。
比如:
Promise.resolve().then(() => {
console.log('a');
Promise.resolve().then(() => console.log('b'));
});
Promise.resolve().then(() => console.log('c'));
输出一定是 a → c → b,不是 a → b → c —— 因为第一个 then 执行时,第二个 then 已入队,但当前微任务队列里已有两个任务(第一个 then 的后续和第三个 then),它们按入队顺序执行;而第二个 then 是在第一个 then 执行中动态加入的,排在队尾。
实际判断小技巧
遇到不确定的任务类型,可套用这个逻辑链:
- 它是不是由浏览器/Node 主动发起的「外部事件」?→ 大概率是宏任务(如定时器、点击、网络响应)
- 它是不是由 JS 内部异步机制(Promise、queueMicrotask)主动安排的「紧随当前操作之后」的动作?→ 大概率是微任务
- 它会不会影响 DOM 更新节奏?→ 微任务常用于避免强制同步布局(如用
queueMicrotask(() => el.focus())替代setTimeout)
优先级不是靠“谁写得早”,而是靠“谁被谁触发”和“引擎如何归类”。记牢队列模型:宏任务 → 微任务(清空)→ 宏任务 → 微任务(清空)…… 循环往复。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










