JavaScript 的事件循环机制是其单线程执行模型的核心。

1. 事件循环基本模型

graph LR
  A[执行栈] --> B[执行当前任务]
  B --> C{任务完成?}
  C -->|是| D[检查微任务队列]
  D --> E[执行所有微任务]
  E --> F[UI渲染]
  F --> G[从宏任务队列取一个任务]
  G --> B

2. 完整事件循环流程

sequenceDiagram
  participant 执行栈
  participant 微任务队列
  participant 宏任务队列

  loop 事件循环
    执行栈->>执行栈: 执行当前同步代码
    执行栈->>宏任务队列: 注册宏任务(setTimeout等)
    执行栈->>微任务队列: 注册微任务(Promise.then等)
    执行栈->>微任务队列: 同步代码执行完后立即清空微任务队列
    微任务队列->>执行栈: 执行所有微任务
    执行栈->>渲染引擎: 可选的UI渲染
    执行栈->>宏任务队列: 取下一个宏任务
  end

3. 核心概念定义

概念描述常见API
执行栈JavaScript 主线程按顺序执行代码的调用栈-
宏任务队列存放待执行的"粗粒度"任务,每次事件循环处理一个宏任务setTimeout, setInterval, I/O, UI渲染
微任务队列存放待执行的"细粒度"任务,必须在当前宏任务结束后立即全部执行Promise.then, MutationObserver, queueMicrotask
UI渲染时机在微任务队列清空后,下一个宏任务前执行requestAnimationFrame

4. 代码执行过程分解

示例代码:

console.log('A');

setTimeout(() => {
  console.log('B');
}, 0);

Promise.resolve().then(() => {
  console.log('C');
});

console.log('D');

执行步骤:

  1. 同步阶段

    • 执行栈顺序执行:

      A
      D
    • 注册宏任务 B 到宏任务队列
    • 注册微任务 C 到微任务队列
  2. 微任务阶段

    • 执行所有微任务:

      C
  3. 宏任务阶段

    • 从宏任务队列取出第一个任务:

      B

最终输出:

A
D
C
B

5. Node.js 与浏览器的差异

环境同步代码执行机制特殊案例
浏览器直接执行,不属于任务队列<script> 整体是初始宏任务
Node.js直接执行,不属于任务队列process.nextTick 优先级高于微任务

6. 总结

  • 执行栈(Execution Context Stack):所有同步任务都在主线程上执行,形成一个执行栈。
  • 任务队列(Task Queue):包含宏任务队列和微任务队列,用于存放异步任务的回调。
  • 事件循环(Event Loop):负责在执行栈为空时,从任务队列中取出任务执行,确保异步任务被及时处理。

    • 同步代码 宏任务
    • 事件循环从宏任务队列取任务,但同步代码直接执行
    • 微任务在当前同步代码执行完后立即执行
    • UI渲染发生在微任务队列清空后、下一个宏任务前

SuRuiGit
264 声望23 粉丝