JavaScript中的事件循环与任务队列(Event Loop & Task Queues)
字数 1315 2025-11-12 18:43:10

JavaScript中的事件循环与任务队列(Event Loop & Task Queues)

1. 事件循环的基本概念

JavaScript是单线程语言,但需要处理异步操作(如定时器、网络请求、用户交互等)。事件循环(Event Loop)是协调同步任务和异步任务的机制,确保代码按预期顺序执行。

核心组成

  • 调用栈(Call Stack):执行同步代码,遵循后进先出(LIFO)原则。
  • 任务队列(Task Queue):存放待执行的异步任务回调。
  • 事件循环:监控调用栈和任务队列,当调用栈为空时,将任务队列中的任务推入栈中执行。

2. 任务队列的类型

任务队列分为两种优先级:

  1. 宏任务队列(MacroTask Queue)

    • 包含:setTimeoutsetInterval、I/O操作、UI渲染、事件回调(如点击事件)。
    • 特点:每次事件循环只执行一个宏任务。
  2. 微任务队列(MicroTask Queue)

    • 包含:Promise.then/catch/finallyMutationObserverqueueMicrotask
    • 特点:优先级高于宏任务,当调用栈清空后,会连续执行所有微任务,直到微任务队列为空。

3. 事件循环的执行流程

  1. 执行全局同步代码(属于宏任务),逐行压入调用栈。
  2. 遇到异步任务
    • 宏任务(如setTimeout)将其回调函数加入宏任务队列。
    • 微任务(如Promise.then)将其回调加入微任务队列。
  3. 同步代码执行完毕,调用栈为空。
  4. 检查微任务队列,依次执行所有微任务(包括微任务中产生的新的微任务)。
  5. 微任务队列清空后,执行下一个宏任务(从宏任务队列中取一个)。
  6. 循环步骤4-5,直到所有任务执行完毕。

4. 示例代码分析

console.log("Start");

setTimeout(() => console.log("Timeout"), 0);

Promise.resolve().then(() => console.log("Promise"));

console.log("End");

执行步骤分解

  1. 同步代码执行:
    • 输出 Start
    • setTimeout 回调加入宏任务队列
    • Promise.then 回调加入微任务队列
    • 输出 End
  2. 调用栈为空,检查微任务队列:
    • 执行 Promise.then 回调,输出 Promise
  3. 微任务队列清空,执行下一个宏任务:
    • 输出 Timeout

结果

Start
End
Promise
Timeout

5. 复杂场景:嵌套任务

setTimeout(() => console.log("Timeout 1"), 0);

Promise.resolve().then(() => {
    console.log("Promise 1");
    setTimeout(() => console.log("Timeout 2"), 0);
});

Promise.resolve().then(() => console.log("Promise 2"));

执行顺序

  1. 同步代码:将 setTimeout 回调加入宏任务队列,两个 Promise.then 加入微任务队列。
  2. 清空微任务队列:
    • 输出 Promise 1,将内部的 setTimeout 回调加入宏任务队列。
    • 输出 Promise 2
  3. 执行宏任务队列:
    • 第一个宏任务:输出 Timeout 1
    • 第二个宏任务(来自微任务):输出 Timeout 2

结果

Promise 1
Promise 2
Timeout 1
Timeout 2

6. 注意事项

  • 微任务优先级最高:即使在宏任务中产生的微任务,也会在当前宏任务结束后立即执行。
  • 避免阻塞:长时间同步代码会阻塞事件循环,导致界面卡顿(例如循环内大量计算)。
  • Web Workers:可分流复杂任务到后台线程,避免影响主线程事件循环。

通过理解事件循环的优先级和执行顺序,可以更精准地控制异步代码的逻辑,避免因执行顺序问题导致的Bug。

JavaScript中的事件循环与任务队列(Event Loop & Task Queues) 1. 事件循环的基本概念 JavaScript是单线程语言,但需要处理异步操作(如定时器、网络请求、用户交互等)。事件循环(Event Loop)是协调同步任务和异步任务的机制,确保代码按预期顺序执行。 核心组成 : 调用栈(Call Stack) :执行同步代码,遵循后进先出(LIFO)原则。 任务队列(Task Queue) :存放待执行的异步任务回调。 事件循环 :监控调用栈和任务队列,当调用栈为空时,将任务队列中的任务推入栈中执行。 2. 任务队列的类型 任务队列分为两种优先级: 宏任务队列(MacroTask Queue) : 包含: setTimeout 、 setInterval 、I/O操作、UI渲染、事件回调(如点击事件)。 特点:每次事件循环只执行一个宏任务。 微任务队列(MicroTask Queue) : 包含: Promise.then/catch/finally 、 MutationObserver 、 queueMicrotask 。 特点:优先级高于宏任务,当调用栈清空后,会连续执行所有微任务,直到微任务队列为空。 3. 事件循环的执行流程 执行全局同步代码 (属于宏任务),逐行压入调用栈。 遇到异步任务 : 宏任务(如 setTimeout )将其回调函数加入宏任务队列。 微任务(如 Promise.then )将其回调加入微任务队列。 同步代码执行完毕 ,调用栈为空。 检查微任务队列 ,依次执行所有微任务(包括微任务中产生的新的微任务)。 微任务队列清空后 ,执行下一个宏任务(从宏任务队列中取一个)。 循环步骤4-5 ,直到所有任务执行完毕。 4. 示例代码分析 执行步骤分解 : 同步代码执行: 输出 Start setTimeout 回调加入宏任务队列 Promise.then 回调加入微任务队列 输出 End 调用栈为空,检查微任务队列: 执行 Promise.then 回调,输出 Promise 微任务队列清空,执行下一个宏任务: 输出 Timeout 结果 : 5. 复杂场景:嵌套任务 执行顺序 : 同步代码:将 setTimeout 回调加入宏任务队列,两个 Promise.then 加入微任务队列。 清空微任务队列: 输出 Promise 1 ,将内部的 setTimeout 回调加入宏任务队列。 输出 Promise 2 。 执行宏任务队列: 第一个宏任务:输出 Timeout 1 。 第二个宏任务(来自微任务):输出 Timeout 2 。 结果 : 6. 注意事项 微任务优先级最高 :即使在宏任务中产生的微任务,也会在当前宏任务结束后立即执行。 避免阻塞 :长时间同步代码会阻塞事件循环,导致界面卡顿(例如循环内大量计算)。 Web Workers :可分流复杂任务到后台线程,避免影响主线程事件循环。 通过理解事件循环的优先级和执行顺序,可以更精准地控制异步代码的逻辑,避免因执行顺序问题导致的Bug。