JavaScript事件循环执行顺序是:同步代码→微任务→宏任务。同步代码执行完后立即清空微任务队列(含新产生的微任务),再取一个宏任务执行,如此循环;微任务优先级高于宏任务,Promise.then等总在当前宏任务结束后、下一个宏任务开始前全部执行完毕。

理解 JavaScript 执行机制,关键不是背概念,而是看清「谁先跑、谁等谁」的真实顺序。异步任务队列就是这个顺序的骨架——它不神秘,但必须分清宏任务和微任务这两层排队规则。
宏任务是「一轮事件循环」的起点
宏任务代表一次完整执行周期的入口,比如:
• setTimeout/setInterval 回调
• script 标签整体代码(首次加载时)
• I/O 操作完成后的回调(如 fetch 响应)
• UI 渲染(浏览器在宏任务后、下一轮前可能插入)
每次事件循环只取一个宏任务执行,执行完立刻进入下一步:清空当前所有微任务。
微任务是「紧贴同步代码之后」的快速响应层
微任务优先级更高,总在当前宏任务结束、下一个宏任务开始前被执行,且一次性全部执行完,不会中途插队。常见类型包括:
• Promise.then/catch/finally 的回调
• MutationObserver 的变化通知
• process.nextTick(仅 Node.js)
立即学习“Java免费学习笔记(深入)”;
注意:哪怕在 Promise 回调里再 new 一个 Promise,它的 then 也会被推入本轮微任务队列末尾,仍会在本轮执行完——不是“下一轮”。
执行流程就是「同步 → 微任务 → 宏任务」的循环
实际运行时,JS 引擎按固定节奏推进:
• 先跑完所有同步代码(形成调用栈,不可中断)
• 立刻检查并执行微任务队列,直到为空
• 再从宏任务队列取一个任务执行(比如 setTimeout 回调)
• 重复上述两步
这个节奏决定了为什么 console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); 输出一定是 1 → 3 → 2 ——不是因为 setTimeout 设了 0,而是因为它属于下一轮宏任务,而 Promise.then 属于本轮微任务。
DOM 更新与渲染也受队列约束
浏览器把 UI 渲染当作一个隐式宏任务,通常安排在「上一个宏任务结束 + 所有微任务清空」之后、下一个宏任务开始之前。这意味着:
• 多次修改 DOM 属性,只要都在同一宏任务内,浏览器不会逐次重绘
• 用 MutationObserver 监听变更,它属于微任务,能比 setTimeout 更早响应 DOM 变化
• 想强制触发重排重绘?得把操作放到下一个宏任务(比如用 setTimeout),否则会被合并或延迟


















