news 2026/4/3 4:48:44

JavaScript 事件循环机制详解及项目中的应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript 事件循环机制详解及项目中的应用

第一部分:基础概念

1. JavaScript 执行环境

JavaScript 是单线程的,这意味着它一次只能执行一个任务。为了处理异步操作,JavaScript 使用事件循环机制。

2. 核心组件

  • 调用栈(Call Stack):执行同步代码的地方
  • 任务队列(Task Queue):分为宏任务队列和微任务队列
  • 事件循环(Event Loop):协调调用栈和任务队列的机制

第二部分:举例详细解析

console.log('1. 同步任务开始');setTimeout(()=>{console.log('2. setTimeout 回调');},0);Promise.resolve().then(()=>{console.log('3. Promise.then 回调');});console.log('4. 同步任务结束');

执行步骤分析:

第1步:同步任务执行
  1. console.log('1. 同步任务开始')压入调用栈,立即执行,输出1
  2. setTimeout压入调用栈,Web API 开始计时(0ms),回调函数放入宏任务队列
  3. Promise.resolve().then()压入调用栈,.then()的回调函数放入微任务队列
  4. console.log('4. 同步任务结束')压入调用栈,立即执行,输出4

此时状态:

  • 调用栈:空
  • 微任务队列[Promise.then回调]
  • 宏任务队列[setTimeout回调]
第2步:事件循环检查
  1. 调用栈为空,事件循环开始工作
  2. 优先检查微任务队列,发现有一个任务
  3. 执行微任务:console.log('3. Promise.then 回调'),输出3
  4. 微任务队列清空
第3步:继续事件循环
  1. 微任务队列为空,现在检查宏任务队列
  2. 执行宏任务:setTimeout回调,输出2
  3. 宏任务队列清空

最终输出顺序:1 4 3 2

console.log('script start')asyncfunctionasync1(){awaitasync2()console.log('async1 end')}asyncfunctionasync2(){console.log('async2 end')}async1()setTimeout(function(){console.log('setTimeout')},0)newPromise(resolve=>{console.log('Promise')resolve()}).then(function(){console.log('Promise1')})

关键概念:async/await

  • async函数总是返回一个 Promise
  • await会暂停 async 函数的执行,直到 Promise 解决
  • await后面的代码相当于放在.then()中,属于微任务

执行步骤分析:

第1步:同步任务执行
  1. console.log('script start')→ 输出script start

  2. 定义函数async1async2(不执行)

  3. 调用async1()

    • 进入async1,遇到await async2()
    • 调用async2()console.log('async2 end')→ 输出async2 end
    • await暂停执行,console.log('async1 end')被包装成微任务放入微任务队列
  4. setTimeout→ 回调函数放入宏任务队列

  5. 执行new Promise

    • console.log('Promise')是同步代码 → 输出Promise
    • resolve()执行,.then()的回调放入微任务队列

此时状态:

  • 调用栈:空
  • 微任务队列[async1 end, Promise1](注意顺序!)
  • 宏任务队列[setTimeout回调]
第2步:事件循环检查微任务
  1. 调用栈为空,执行微任务

  2. 按入队顺序执行微任务:

    • 第一个微任务:console.log('async1 end')→ 输出async1 end
    • 第二个微任务:console.log('Promise1')→ 输出Promise1
  3. 微任务队列清空

第3步:执行宏任务
  1. 执行setTimeout回调 → 输出setTimeout

最终输出顺序:script start → async2 end → Promise → async1 end → Promise1 → setTimeout

那么到此,应该是可以理解到事件循环的感觉了,那接下来我们就开始看看事件循环的完整逻辑

1. 任务分类

宏任务(Macrotasks)
  • setTimeoutsetInterval
  • setImmediate(Node.js)
  • requestAnimationFrame(浏览器)
  • I/O 操作
  • UI 渲染(浏览器)
  • 主线程的 script 标签内容
微任务(Microtasks)
  • Promise.then().catch().finally()
  • process.nextTick()(Node.js,优先级最高)
  • MutationObserver(浏览器)
  • queueMicrotask()
  • async/await的后续代码

2. 事件循环执行顺序

1. 执行一个宏任务(script标签内容) 2. 执行过程中遇到异步任务: - 宏任务 → 放入宏任务队列 - 微任务 → 放入微任务队列 3. 当前宏任务执行完毕 4. 检查微任务队列,依次执行所有微任务 5. 如有必要,进行UI渲染 6. 从宏任务队列取出下一个宏任务执行 7. 回到步骤3,形成循环

3. 重要规则

规则1:微任务优先
  • 每执行完一个宏任务,都要清空所有微任务
  • 微任务执行期间产生的新微任务会加入当前队列,并在本次循环中执行
规则2:async/await 转化

javascript

asyncfunctionexample(){awaitfoo()// 相当于 Promise.resolve(foo()).then(...)console.log('A')// 这部分在微任务队列中}
规则3:多个任务队列
  • 宏任务可能有多个来源(定时器、I/O等),有各自的队列
  • 微任务只有一个队列,按入队顺序执行

在举例理解一下

javascript

// 测试微任务嵌套Promise.resolve().then(()=>{console.log('微任务1');Promise.resolve().then(()=>{console.log('微任务中的微任务');});}).then(()=>{console.log('微任务2');});// 输出顺序:微任务1 → 微任务中的微任务 → 微任务2

javascript

// 测试多个宏任务setTimeout(()=>console.log('宏任务1'),10);Promise.resolve().then(()=>console.log('微任务1'));setTimeout(()=>{console.log('宏任务2');Promise.resolve().then(()=>console.log('宏任务2中的微任务'));},1);Promise.resolve().then(()=>console.log('微任务2'));setTimeout(()=>{console.log('宏任务3');Promise.resolve().then(()=>console.log('宏任务3中的微任务'));},0);结果: 微任务1微任务2宏任务3宏任务3中的微任务 宏任务1宏任务2宏任务2中的微任务 为什么呢?聪明的你已经会了 一开始 微任务 放入 微任务1, 微任务2;然后宏任务放入 宏任务3这个时候, 计时器还没有到底100ms的时候, 打印微任务1、微任务2;然后微任务清空,开始 宏任务3, 放入微任务 宏任务3中的微任务,然后打印宏任务3中的微任务; 然后计时器到了,打印宏任务1、宏任务2、放入微任务, 打印宏任务2中的微任务;大概就是这种感觉

总结

  1. 同步任务立即执行
  2. 微任务宏任务优先级高
  3. 每个宏任务执行后,都要清空所有微任务
  4. async/await本质是 Promise 的语法糖,await后面的代码是微任务
  5. 事件循环确保了 JavaScript 的单线程能够处理异步操作
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 14:03:36

个人博客作业 3

一、反思个人博客 1的5个问题 https://blog.csdn.net/bboy_lemon/article/details/153042340?fromshareblogdetail&sharetypeblogdetail&sharerId153042340&sharereferPC&sharesourcebboy_lemon&sharefromfrom_link 问题1:初创团队怎么吸引到…

作者头像 李华
网站建设 2026/3/26 0:24:59

当LCL逆变器遇上谐波:两个前馈策略的实战PK

可基于电容电流前馈与电网电压全前馈的三相LCL并网逆变器谐波抑制MATLAB仿真。 搭建了LCL型三相并网逆变器仿真模型模型。 在此基础上,对电网电压背景谐波引起的电流谐波及其抑制方法进行了研究,对比分析了电网电压比例前馈控制策略和电网电压全前馈控制…

作者头像 李华
网站建设 2026/4/1 4:57:06

3分钟掌握downkyi下载优先级:让你的重要视频先下载

还在为下载队列中重要视频被卡在最后而烦恼吗?downkyi作为哔哩哔哩视频下载神器,其智能优先级系统能让你完全掌控下载顺序,确保重要内容优先完成!无论你是学习爱好者还是收藏控,这套系统都能大幅提升你的下载效率。 【…

作者头像 李华
网站建设 2026/4/2 14:39:31

10、基于Z世代研究的雇主品牌商业模型剖析

基于Z世代研究的雇主品牌商业模型剖析 1. 不同世代在职场的差异 在职场中,不同世代之间的差异一直存在。如今,同一工作场所中活跃世代之间的差距达到了前所未有的程度。这主要是因为工业社会逐渐被信息社会和知识社会所取代,彻底改变了人们的沟通方式。互联网让信息获取变…

作者头像 李华
网站建设 2026/3/24 20:43:57

字节跳动Seed-OSS-36B震撼开源:512K超长上下文引领大模型效率革命

2025年8月20日夜,字节跳动旗下Seed实验室对外发布重磅消息,正式将其研发的Seed-OSS系列大语言模型纳入开源生态。此次披露的模型家族包含三个核心版本:具备合成数据训练的Seed-OSS-36B-Base、纯真实数据训练的Seed-OSS-36B-Base,以…

作者头像 李华