微任务包括promise.then/catch/finally回调、mutationobserver回调和queuemicrotask()入队函数,它们由微任务队列统一管理,在当前宏任务结束后立即、依次、全部执行完毕。

微任务(Microtask)是 JavaScript 事件循环中优先级高于宏任务(Macrotask)的一类异步操作,它们会在当前同步代码执行完、下一次宏任务开始前**立即、依次、全部执行完毕**。Promise.then 是最典型的微任务来源,它的调度机制直接体现了微任务队列的运行逻辑。
常见的微任务有哪些?
微任务由 JavaScript 引擎维护的微任务队列(Microtask Queue)统一管理,以下是最主要的几类:
- Promise.then/catch/finally 的回调函数(只要 Promise 状态已确定,其回调就会被加入微任务队列)
- MutationObserver 的回调(监听 DOM 变化后触发)
- queueMicrotask() 显式入队的函数(ES2021 标准 API,用于手动调度微任务)
- Object.observe(已废弃) 和部分浏览器内部 API(如某些 V8 内部机制),但实际开发中基本不涉及
Promise.then 是怎么进入并执行微任务队列的?
Promise.then 不是“立刻执行”,也不是“推给定时器”,而是在 Promise 状态变为 fulfilled 或 rejected 后,将回调函数同步注册进微任务队列。关键点在于:
- 如果 Promise 已处于 fulfilled/rejected 状态(比如通过 Promise.resolve(1) 创建),调用 .then 时回调会立即入队,但不会立刻执行——要等当前同步栈清空
- 如果 Promise 还在 pending 状态(比如 new Promise((r) => setTimeout(r, 0))),.then 回调会先被缓存;当状态改变时,再同步触发入队
- 微任务队列在每次宏任务(如 script 执行、setTimeout 回调、I/O 回调)结束后被清空:引擎会持续取出并执行微任务,直到队列为空,才继续下一个宏任务
微任务和宏任务的典型执行顺序
理解调度的关键是记住这个流程:同步代码 → 微任务队列(清空)→ 渲染(可选)→ 下一个宏任务。例如:
console.log(1); Promise.resolve().then(() => console.log(2)); setTimeout(() => console.log(3), 0); console.log(4); // 输出:1 → 4 → 2 → 3
解释:
– 1 和 4 是同步代码,立刻输出;
– then 回调作为微任务,在同步代码结束后、setTimeout 前执行;
– setTimeout 是宏任务,排在下一轮事件循环。
为什么微任务不能无限嵌套?
虽然微任务会连续执行,但引擎有保护机制:如果某个微任务又创建了新微任务(比如在 then 里再调用 Promise.then),这些新微任务仍会加入当前微任务队列尾部,继续执行——这可能导致“微任务风暴”。浏览器通常限制连续执行微任务的数量(如 Chrome 限制约 1000 层),避免阻塞主线程。实际开发中应避免在微任务中无终止地递归调度。











