事件执行阶段由事件循环决定,而非事件机制本身;它通过宏任务(如setTimeout)和微任务(如Promise.then)的调度形成两层阶段:每轮仅执行一个宏任务,随后立即清空所有当前微任务。

JavaScript 中事件机制本身不直接控制“事件执行的阶段”,真正决定异步代码执行时序的是事件循环(Event Loop),它通过调度宏任务(Macrotask)和微任务(Microtask)来划分执行阶段。所谓“阶段”,本质上是事件循环每次循环中对任务队列的处理顺序。
宏任务与微任务构成两个关键执行阶段
事件循环每轮只执行一个宏任务,但会在该宏任务结束后立即、连续执行完所有当前可得的微任务——这形成了天然的两层阶段:
-
宏任务阶段:如
script全局代码、setTimeout、setInterval、I/O 回调、UI 渲染(浏览器)、setImmediate(Node.js)等。每个宏任务开启一次新的“循环起点”。 -
微任务阶段:紧随每个宏任务之后执行,包括
Promise.then/catch/finally、MutationObserver(浏览器)、process.nextTick(Node.js,优先级最高)。它不会被中断,新产生的微任务也会在本轮清空。
阶段切换由事件循环自动触发,不可手动插入
你不能用代码“跳到下一阶段”或“暂停当前阶段”,但可以通过任务类型选择进入哪个阶段:
- 想让逻辑尽快执行(但不阻塞当前同步流程)→ 用
Promise.resolve().then(...)或queueMicrotask(...) - 想让逻辑延后到下一个事件循环开始时执行→ 用
setTimeout(..., 0)或postMessage(跨上下文) - 避免意外嵌套导致的微任务风暴(如递归
then)→ 可改用setTimeout主动退到宏任务,打断连续微任务链
浏览器中还存在渲染相关的隐式阶段
在浏览器环境,事件循环在微任务之后、下一个宏任务之前,会判断是否需要 UI 更新:
- 若 DOM 有变更且页面处于活跃状态,浏览器可能插入渲染阶段(样式计算 → 布局 → 绘制)
-
requestAnimationFrame回调会在**下次渲染前**执行,属于独立于宏/微任务的渲染阶段钩子 -
requestIdleCallback则在浏览器空闲时触发,属于低优先级的后台阶段
实际调试时可借助执行顺序反推阶段位置
例如这段代码能清晰暴露阶段边界:
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
console.log('4'); // 同步输出为 1 → 4 → 3 → 2,说明:
① 同步代码为第一阶段;
② 同步结束后立即进入微任务阶段(3);
③ 微任务清空后才进入下一个宏任务阶段(2)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











