JavaScript事件循环按“同步→微任务(全清)→宏任务(一个)”三步循环执行;同步代码先运行,随后立即清空所有微任务(如Promise.then),再执行一个宏任务(如setTimeout),以此保证异步顺序。

JavaScript 的事件循环是它实现异步编程的底层调度机制,核心在于“单线程不阻塞”——同步代码优先执行,异步任务交由宿主环境(如浏览器或 Node.js)托管,再按规则分批回调到主线程。
调用栈空了,事件循环才开始干活
JavaScript 引擎执行代码时,所有同步函数都压入调用栈(Call Stack),按顺序执行、弹出。一旦栈变空,事件循环立刻介入:不是马上挑一个异步任务来跑,而是先检查有没有微任务等着处理。
- 微任务(Microtask)必须全部清空——比如 Promise.then、queueMicrotask、MutationObserver 回调
- 清完才从宏任务队列(Macrotask Queue)里取一个任务执行(比如 setTimeout 回调、用户点击事件、I/O 完成通知)
- 执行完这一个宏任务后,再次回到“清空微任务”环节,如此循环
宏任务和微任务的典型来源
两类任务由不同机制产生,优先级也不同:
- 宏任务:整体 script 脚本、setTimeout/setInterval、DOM 事件(click、input)、fetch 响应、UI 渲染(浏览器特有)
- 微任务:Promise.then/catch/finally、queueMicrotask()、MutationObserver 回调
注意:process.nextTick 是 Node.js 特有,优先级比 Promise.then 还高,但它不属于标准 Web API。
立即学习“Java免费学习笔记(深入)”;
一次事件循环的完整流程
可以理解为三步固定节奏:
- 执行当前调用栈中所有同步代码(遇到异步操作,只注册回调,不等待)
- 立即执行所有已排队的微任务(一个不剩)
- 执行一个宏任务(仅一个!哪怕队列里有十个 setTimeout,这次也只跑第一个)
然后回到第1步,开启下一轮。这个“同步 → 微任务(全清)→ 宏任务(一个)”的节奏,就是控制异步行为顺序的关键。
为什么这个机制重要?
它直接决定你写的异步代码是否按预期执行。比如下面这段:
console.log('A');setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
输出一定是 A → D → C → B。因为 C 是微任务,在同步代码(A、D)结束后立刻执行;B 是宏任务,要等下一轮循环才轮到。
不复杂但容易忽略











