什么是事件循环(Event Loop)
为什么叫 Event(事件)
在浏览器 / Node 里,大部分异步行为都是 事件驱动的。
比如这些都是 事件:
| 事件 | 示例 |
|---|---|
| 定时器事件 | setTimeout |
| 网络事件 | fetch / ajax |
| 用户事件 | click |
| 文件IO | Node.js |
| Promise完成 | then |
button.addEventListener("click", () => {
console.log("clicked");
});
用户点击按钮 → 触发 click事件。浏览器会把这个事件放到 任务队列。
所以这里的 Event 指的是:各种完成后的异步事件
// 1 秒后产生一个 timer事件。
setTimeout(() => {
console.log("timeout done");
}, 1000);
JS 执行顺序
1 执行同步代码 -------- 主线程任务
2 清空 microtask queue --
3 执行一个 macrotask
4 再清空 microtask
5 循环
宏任务 (Macrotask)
setTimeout
setInterval
setImmediate (node)
I/O
UI render
微任务 (Microtask)
优先级更高。
Promise.then
Promise.catch
queueMicrotask
MutationObserver
process.nextTick (node)
如何理解Loop
JS 引擎会 不停地循环检查任务队列。
执行同步代码
while(true) {
执行 microtask 队列
从 macrotask 取一个任务执行
}
Event Loop:一个不断循环(Loop)处理异步事件(Event)的机制。
为什么 Microtask 必须全部执行完
Promise.resolve()
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3)
})
这段代码逻辑上是:1 → 2 → 3
因为每个 .then() 都依赖前一个。
如果只执行一个 microtask 会怎样?
- 第1轮:microtask: then1,输出1,生成then2
- 但 必须等下一轮 event loop
- 造成:Promise 链被拆散到多个 event loop。
- 异步逻辑(Promise 链)不能连续执行完,而会被其他任务打断。
示例代码
代码
console.log("script start");
setTimeout(() => {
console.log("timeout1");
Promise.resolve().then(() => {
console.log("promise inside timeout1");
});
}, 0);
setTimeout(() => {
console.log("timeout2");
}, 0);
Promise.resolve().then(() => {
console.log("promise1");
});
console.log("script end");
最终输出
script start
script end
promise1
timeout1
promise inside timeout1
timeout2
执行过程
执行 Call Stack(script)
script start script end此时队列:
Microtask Queue --------------- promise1 Macrotask Queue --------------- timeout1 timeout2执行所有 Microtask ( promise1 ),队列变成:
Microtask Queue --------------- 空 Macrotask Queue --------------- timeout1 timeout2执行一个 Macrotask (timeout1),并产生新的 microtask (promise inside timeout1),队列变成:
Microtask Queue --------------- promise inside timeout1 Macrotask Queue --------------- timeout2再执行所有 Microtask (开始Loop)
Microtask Queue --------------- 空 Macrotask Queue --------------- timeout2执行一个 Macrotask:timeout2