事件循环是javaScript实现异步的底层机制,通过分同步代码、微任务、宏任务三阶段调度,确保单线程不阻塞;执行顺序为同步→全部微任务→一个宏任务→渲染→重复。

事件循环是 javascript 实现异步行为的底层调度机制,它让单线程的 js 能“看起来”同时处理多个任务,而不会卡住页面或阻塞用户操作。
为什么必须有事件循环
JavaScript 是单线程语言,主线程既要跑代码,又要渲染页面、响应点击——所有这些都挤在同一个线程里。如果没有事件循环,遇到一个 2 秒的网络请求,整个页面就会冻结 2 秒,按钮点不了、滚动卡死、动画停摆。
事件循环解决了这个问题:它把耗时操作(比如定时器、请求、用户点击)交给浏览器或 node.js 的其他模块去后台处理,JS 主线程只管执行当前代码,等空下来再统一查收“谁干完了”,然后按规则执行回调。
事件循环怎么一步步工作
整个过程可以简化为四步循环:
立即学习“Java免费学习笔记(深入)”;
- 先执行调用栈里的所有同步代码(比如 console.log、函数调用、for 循环)
- 遇到异步操作(如 setTimeout、fetch、promise),不等结果,立刻交给 Web API 或系统模块去处理
- 当异步操作完成,对应回调被放进任务队列——但不是立刻执行,要分两类:微任务(如 Promise.then、MutationObserver)和宏任务(如 setTimeout、setInterval、I/O、ui 渲染)
- 同步代码一结束,事件循环就先清空全部微任务队列,再取一个宏任务执行,然后重复这个流程
宏任务和微任务的执行顺序很关键
这是最容易出错的地方。看这个典型例子:
console.log(‘1’);
setTimeout(() => console.log(‘2’), 0);
Promise.resolve().then(() => console.log(‘3’));
console.log(‘4’);
输出是:1 → 4 → 3 → 2。原因不是“谁设的延时短谁先执行”,而是:
– ‘1’ 和 ‘4’ 是同步代码,立刻执行;
– setTimeout 回调进宏任务队列;
– Promise.then 进微任务队列;
– 同步执行完,事件循环马上执行所有微任务(只有 ‘3’),再取下一个宏任务(’2’)。
浏览器环境里还有个隐藏环节:渲染
在微任务全部执行完、下一个宏任务开始前,浏览器会检查是否需要更新 UI(比如样式变化、动画帧)。这意味着:如果你在 Promise.then 里改了 dom,用户能立刻看到;但如果在 setTimeout 里改,就要等下一轮事件循环,可能多延迟几毫秒甚至一帧(16ms)。
基本上就这些。不复杂,但容易忽略微任务的“插队权”和渲染时机的影响。