微任务在每个宏任务结束后立即清空执行,而宏任务每次仅执行一个;微任务包括promise.then、mutationobserver等,宏任务包括settimeout、script整体代码等。
宏任务与微任务的差异,直接决定了 javascript 代码的实际执行节奏和视觉反馈时机。它们不是简单的“谁先谁后”问题,而是影响异步逻辑可靠性、ui 更新及时性、甚至 bug 隐患的关键机制。
执行时机不同:微任务总在宏任务“夹缝中”执行
每次一个宏任务(比如 setTimeout 回调、整个 script 标签、用户点击事件)执行完,JS 引擎会立刻暂停,把所有排队的微任务(Promise.then、MutationObserver、queueMicrotask)一次性清空,再继续下一个宏任务。
- 这意味着哪怕只写一个
Promise.resolve().then(...),它的回调也一定比紧随其后的setTimeout(..., 0)先打印。 - 微任务不会“等下一轮”,而是在当前宏任务结束的“瞬间”插入执行——这是它能保证链式响应、避免中间状态暴露的核心原因。
队列行为不同:微任务队列必须“清空”,宏任务队列只取一个
微任务队列是“贪婪执行”的:只要队列非空,就持续取任务执行,过程中新产生的微任务(比如在 .then 里又调用 Promise.resolve().then)也会被追加到队尾并继续执行,直到彻底为空。
- 这导致嵌套 Promise 可能一口气执行十几层,不给渲染或其它宏任务插手机会。
- 宏任务队列则严格“一次一单”:每轮事件循环只从队列头部取出一个任务执行(如一个 setTimeout 回调),哪怕队列里已排了五个定时器,也得等这一轮走完、清空微任务、再进下一轮才取第二个。
实际影响体现在三类典型场景
1. UI 更新延迟感知:DOM 修改后立即用 setTimeout 获取 offsetHeight,常拿到旧值;改用 queueMicrotask 或 MutationObserver 就能确保 DOM 已真实重排。
2. 状态竞态风险:连续多个 setState(React)或 ref.value = ...(Vue)若混用宏/微任务,可能造成中间状态被跳过或重复触发;统一用微任务可收敛更新批次。
3. 异步链断裂隐患:在 setTimeout 回调里抛错,若没配 catch,错误会变成未捕获异常;而 Promise.then 抛错会自动进入下一个 catch 微任务——这是微任务自带错误传递保障的体现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











