跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScriptNode.js大前端

JavaScript 事件循环(Event Loop)

JavaScript 事件循环是处理异步的核心机制。调用栈、宏任务与微任务的概念及执行顺序。通过初等和中等难度练习题,解析同步代码、定时器、Promise 的执行优先级。关键点包括:微任务优先于宏任务,Promise 构造函数同步而 .then 异步,setTimeout 即使为 0 也是宏任务。实际场景涉及性能优化、顺序控制及避免阻塞。

竹影清风发布于 2026/3/25更新于 2026/7/348 浏览
JavaScript 事件循环(Event Loop)

JavaScript 事件循环(Event Loop)

什么是事件循环?

事件循环是 JavaScript 实现异步编程的核心机制。JavaScript 是单线程语言,通过事件循环来处理异步操作,避免阻塞主线程。

详解:

JavaScript 在设计之初便是单线程,即指程序运行时,只有一个线程存在,同一时间只能做一件事。 为什么要这么设计,跟 JavaScript 的应用场景有关 JavaScript 初期作为一门浏览器脚本语言,通常用于操作 DOM,如果是多线程,一个线程进行了删除 DOM,另一个添加 DOM,此时浏览器该如何处理? 为了解决单线程运行阻塞问题,JavaScript 用到了计算机系统的一种运行机制,这种机制就叫做事件循环(Event Loop)

核心概念

1. 调用栈(Call Stack)
  • 存储函数执行上下文
  • 遵循后进先出(LIFO)原则
  • 同步代码在这里执行
2. 任务队列(Task Queue)

分为两种:

宏任务(Macro Task)

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O 操作
  • UI 渲染

微任务(Micro Task)

  • Promise.then/catch/finally
  • async/await
  • MutationObserver
  • process.nextTick(Node.js,优先级最高)
3. 执行顺序
  1. 执行同步代码(调用栈)
  2. 执行所有微任务
  3. 执行一个宏任务
  4. 执行所有微任务
  5. 重复步骤 3-4

初等难度练习题

console.log(1);
setTimeout(() => { console.log(2); }, 0);
new Promise((resolve, reject) => {
  console.log('new Promise');
  resolve();
}).then(() => {
  console.log('then');
});
console.log(3);

解题顺序

// 遇到 console.log(1),直接打印 1
// 遇到定时器,属于新的宏任务,留着后面执行
// 遇到 new Promise,这个是直接执行的,打印 'new Promise'
// .then 属于微任务,放入微任务队列,后面再执行
// 遇到 console.log(3) 直接打印 3
// 好了本轮宏任务执行完毕,现在去微任务列表查看是否有微任务,发现 .then 的回调,执行它,打印 'then'
// 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,打印 2

中等难度练习题

console.log('1');
setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => {
    console.log('3');
  });
}, 0);
Promise.resolve().then(() => {
  console.log('4');
  setTimeout(() => {
    console.log('5');
  }, 0);
});
new Promise((resolve) => {
  console.log('6');
  resolve();
}).then(() => {
  console.log('7');
});
setTimeout(() => {
  console.log('8');
  Promise.resolve().then(() => {
    console.log('9');
  });
}, 0);
console.log('10');
题目要求

请写出上述代码的输出顺序,并解释原因。


答案解析

输出顺序: 1 → 6 → 10 → 4 → 7 → 2 → 3 → 8 → 9 → 5

console.log('1'); // 第一轮,同步代码。输出'1'
setTimeout(() => { // 第一轮,宏任务 1 加入队列
  console.log('2'); // 第三轮,执行宏任务 1。输出'2'
  Promise.resolve().then(() => { // 第三轮,立即执行微任务 3。
    console.log('3'); // 第三轮,输出'3'
  });
}, 0);
Promise.resolve().then(() => { // 第一轮,微任务 1 加入队列
  console.log('4'); // 第二轮,执行微任务 1。输出'4'
  setTimeout(() => { // 第二轮,产生新的宏任务 3
    console.log('5'); // 第五轮,执行宏任务 3,输出'5'
  }, 0);
});
new Promise((resolve) => { // 第一轮,同步。构造函数同步执行
  console.log('6'); // 第一轮,输出'6'。
  resolve();
}).then(() => { // 第一轮,微任务 2 加入队列
  console.log('7'); // 第二轮,执行微任务 2,输出'7'
});
setTimeout(() => { // 第一轮,宏任务 2 加入队列。
  console.log('8'); // 第四轮,执行宏任务 2。输出'8'
  Promise.resolve().then(() => { // 第四轮,产生微任务 4。
    console.log('9'); // 第四轮,立即执行微任务 4,输出'9'
  });
}, 0);
console.log('10'); // 第一轮,同步代码。输出'10'
详细执行过程

第一轮(同步代码):

  1. console.log('1') → 输出 1
  2. setTimeout(...) → 宏任务 1 加入队列
  3. Promise.resolve().then(...) → 微任务 1 加入队列
  4. new Promise(...) 构造函数同步执行 → 输出 6
  5. .then(...) → 微任务 2 加入队列
  6. setTimeout(...) → 宏任务 2 加入队列
  7. console.log('10') → 输出 10

第二轮(清空微任务):

  1. 执行微任务 1:输出 4,同时产生新的宏任务 3
  2. 执行微任务 2:输出 7

第三轮(执行宏任务 1):

  1. 输出 2,产生微任务 3
  2. 立即执行微任务 3:输出 3

第四轮(执行宏任务 2):

  1. 输出 8,产生微任务 4
  2. 立即执行微任务 4:输出 9

第五轮(执行宏任务 3):

  1. 输出 5
关键点总结
  1. Promise 构造函数是同步的,只有.then是异步的
  2. 微任务优先级高于宏任务,每次宏任务后都会清空所有微任务
  3. setTimeout 即使延迟为 0,也会被放入宏任务队列
  4. 在宏任务中产生的微任务会在该宏任务执行完后立即执行

实际应用场景

1. 优化性能
// 使用微任务批量更新 DOM
let updates = [];
function scheduleUpdate(data) {
  updates.push(data);
  Promise.resolve().then(() => {
    // 批量处理所有更新
    processBatch(updates);
    updates = [];
  });
}
2. 确保执行顺序
async function fetchData() {
  const data = await fetch('/api/data');
  // 这里的代码会在微任务中执行
  console.log('数据获取完成');
}
3. 避免阻塞
function heavyTask() {
  setTimeout(() => {
    // 将耗时操作放入宏任务
    // 避免阻塞 UI 渲染
    performHeavyComputation();
  }, 0);
}

常见面试问题

  1. 微任务和宏任务的区别?
    • 微任务在当前事件循环结束前执行,宏任务在下一个事件循环执行
    • 微任务优先级更高
  2. async/await 的执行顺序?
    • await 后面的代码相当于放在 Promise.then 中
  3. Node.js 中的 process.nextTick?
    • 优先级高于所有微任务,在当前操作完成后立即执行

参考资源

  • MDN - Event Loop
  • Jake Archibald - Tasks, microtasks, queues and schedules

目录

  1. JavaScript 事件循环(Event Loop)
  2. 什么是事件循环?
  3. 核心概念
  4. 1. 调用栈(Call Stack)
  5. 2. 任务队列(Task Queue)
  6. 3. 执行顺序
  7. 初等难度练习题
  8. 解题顺序
  9. 中等难度练习题
  10. 题目要求
  11. 答案解析
  12. 详细执行过程
  13. 关键点总结
  14. 实际应用场景
  15. 1. 优化性能
  16. 2. 确保执行顺序
  17. 3. 避免阻塞
  18. 常见面试问题
  19. 参考资源
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 图像畸变矫正原理及 MATLAB 与 FPGA 实现
  • C++ 内存管理:new/delete 原理及与 malloc/free 对比
  • Vue 3 编程的 10 个实用技巧
  • Trae 集成 Figma MCP 实现前端代码自动生成
  • VS Code 前端开发必备 10 款插件及配置指南
  • Java 流程控制:从条件判断到循环遍历
  • CycleGAN 详解与实现
  • 为什么 AI 开发首选 Python 而非 Java 或 C++?
  • 解决 Java 源发行版 17 与目标发行版不匹配问题
  • AI 鉴伪技术解析:人脸视频、AIGC 图像及文档篡改检测
  • C++ STL 容器常用函数与实战技巧
  • 滑动窗口算法详解:经典例题解析
  • 向量数据库与 RAG 应用架构解析(上)
  • Java 社区跑腿家政上门服务商城解决方案
  • 基于 Leaflet 和天地图的长沙免费运动场所 WebGIS 可视化
  • Windows 系统下 Stable Diffusion WebUI 环境搭建指南
  • CosyVoice3 支持 ARPAbet 音素标注,提升英文发音准确性
  • C++ 哈希表底层实现:unordered_map/set、位图与布隆过滤器
  • Python 开发 MongoDB 数据库 MCP Server 实战指南
  • Stable Diffusion WebUI 无障碍改造:键盘导航与屏幕阅读器适配

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online