【前端】JavaScript 的事件循环 (Event Loop)

JavaScript 的事件循环 (Event Loop ) 是其实现异步编程的核心机制,即使 JS 是单线程语言,它也能高效处理 I/O、网络请求、计时器等非阻塞操作。以下是其工作原理的精要解析:


核心概念

  1. 单线程执行

    • JS 引擎(如 V8)只能顺序执行一个任务。
    • 异步行为需要靠宿主环境(浏览器/Node.js)提供的事件循环调度。
  2. 任务队列 (Task Queue)

    • 所有异步操作完成后对应的回调函数会进入队列等待执行。
    • 队列类型包括:
      • 宏任务队列 (Macrotask Queue)script(整体代码)、setTimeoutsetInterval、DOM 事件回调、I/O 操作。
      • 微任务队列 (Microtask Queue)Promise.then()await后续代码、MutationObserverqueueMicrotask()

事件循环执行流程

  1. 执行同步代码

    运行主线程的同步任务,形成执行栈(Call Stack),若遇到异步操作(如 setTimeoutfetch),交给 Web API 处理。

  2. 异步任务回调入队

    异步操作完成后,Web API 将其回调推入对应队列:

    • 宏任务回调 → 宏任务队列
    • 微任务回调 → 微任务队列
  3. 轮询事件队列(关键步骤)

    • 执行完当前所有同步任务 后,按优先级处理队列:
      1. 清空微任务队列
        将所有可执行的微任务按顺序执行直至队列为空(执行中可能产生新微任务)。
      2. 执行一个宏任务
        从宏任务队列中取出最早的一个任务执行。
      3. 再次清空微任务队列
        执行该宏任务过程中产生的所有微任务。
      4. 更新渲染(浏览器环境下)
        如有需要,执行 UI 渲染、布局计算等。
    • 循环:重复步骤 3,直至所有队列为空。

执行顺序示例代码解析

javascript 复制代码
console.log("1. 同步代码开始");

setTimeout(() => {
  console.log("4. 宏任务 (setTimeout)");
}, 0);

Promise.resolve().then(() => {
  console.log("3. 微任务 (Promise)");
});

console.log("2. 同步代码结束");

输出顺序
1. 同步代码开始2. 同步代码结束3. 微任务 (Promise)4. 宏任务 (setTimeout)

原因

  • 先执行完所有同步代码(输出 1 和 2)。
  • 微任务队列优先于宏任务队列执行(Promise.then()setTimeout 前)。

为什么区分宏任务与微任务?

  • 微任务的优先级更高
    确保诸如 Promise 状态更新后能立即触发回调,提升用户体验(如快速响应用户操作)。
  • 避免渲染阻塞
    浏览器在宏任务之间插入 UI 渲染流程,而微任务在渲染前全部执行,保证页面及时更新。

事件循环流程图

复制代码
     同步代码执行
         ↓
  遇到异步操作 → Web API 处理 → 完成后回调入队
         ↓                      ↗ 宏任务队列
         |                     /
事件循环开始                 ↓
         |          下一轮循环  取出最早宏任务 → 执行
         |              ↑          ↓
         |------→ 清空微任务队列 → 可能产生新微任务
                       |
                   (浏览器环境下)
                        ↓
                    需要渲染? → 执行渲染流程

⚠️ 常见误区

  1. setTimeout(0) 并非立即执行
    定时器时间仅表示最小延迟,实际回调需等待同步任务及队列中的任务完成。
  2. 微任务会阻塞页面渲染
    长时间的微任务链会导致 UI 冻结。
  3. Node.js 与浏览器的事件循环差异
    Node.js 的事件循环分为更多阶段(如 nextTick 优先级最高)。

掌握事件循环机制,能帮你精准控制代码时序、避免竞态条件,并理解框架如 Vue/React 的底层调度原理!

相关推荐
无敌最俊朗@5 小时前
C++ 序列容器深度解析:vector、deque 与 list
开发语言·数据结构·数据库·c++·qt·list
Da Da 泓5 小时前
LinkedList模拟实现
java·开发语言·数据结构·学习·算法
cxr8285 小时前
BMAD框架实践:掌握story-checklist提升用户故事质量
前端·人工智能·agi·智能体·ai赋能
emma羊羊6 小时前
【xsslabs】第12-19关
前端·javascript·靶场·xss
Humbunklung6 小时前
VC++ 使用OpenSSL创建RSA密钥PEM文件
开发语言·c++·openssl
Humbunklung6 小时前
填坑:VC++ 采用OpenSSL 3.0接口方式生成RSA密钥
开发语言·c++·rsa·openssl 3.0
zl21878654487 小时前
Playwright同步、异步、并行、串行执行效率比较
开发语言·python·测试工具
Larry_Yanan8 小时前
QML学习笔记(十七)QML的属性变更信号
javascript·c++·笔记·qt·学习·ui
Tony Bai8 小时前
【Go开发者的数据库设计之道】05 落地篇:Go 语言四种数据访问方案深度对比
开发语言·数据库·后端·golang
gopyer8 小时前
180课时吃透Go语言游戏后端开发3:Go语言中其他常用的数据类型
开发语言·游戏·golang·游戏后端开发