JS:EventLoop事件循环:异步事件处理机制

什么是事件循环(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

执行过程

  1. 执行 Call Stack(script)

    script start
    script end
    

    此时队列:

    Microtask Queue
    ---------------
    promise1
    
    Macrotask Queue
    ---------------
    timeout1
    timeout2
    
  2. 执行所有 Microtask ( promise1 ),队列变成:

    Microtask Queue
    ---------------
    空
    
    Macrotask Queue
    ---------------
    timeout1
    timeout2
    
  3. 执行一个 Macrotask (timeout1),并产生新的 microtask (promise inside timeout1),队列变成:

    Microtask Queue
    ---------------
    promise inside timeout1
    
    Macrotask Queue
    ---------------
    timeout2
    
  4. 再执行所有 Microtask (开始Loop)

    Microtask Queue
    ---------------
    空
    
    Macrotask Queue
    ---------------
    timeout2
    
  5. 执行一个 Macrotask:timeout2

×

喜欢就点赞,疼爱就打赏