深入理解JavaScript事件循环Event Loop:宏任务与微任务的奇幻之旅

news2024/10/6 2:24:49

在这里插入图片描述

🔥 个人主页:空白诗

在这里插入图片描述

文章目录

  • 🎉 引言
    • 🌟 什么是事件循环?
    • 📚 「宏任务」 vs 「微任务」
      • 「宏任务」(Macrotask)
      • 「微任务」(Microtask)
      • 实际应用中的注意事项
    • 🔀 执行流程概览
    • 📝 代码示例
    • 🔍 深入理解「微任务」执行时机
      • 「微任务」的执行流程特点
      • 示例代码加深理解:
    • 💡 实践建议
      • 1. 避免过度依赖「微任务」
      • 2. 控制异步逻辑复杂度
      • 3. 注意异步操作对UI更新的影响
      • 4. 性能监控与调试
    • 🎖 总结
    • 🔗 相关链接

🎉 引言

JavaScript的世界里,一切皆为异步编程的魔法所驱动,而事件循环(Event Loop)正是这魔法背后的神秘引擎🔍。它确保了我们的代码能够高效、有序地执行,即使面对复杂的异步操作也能游刃有余。本文将带你深入探索这一机制,通过理论结合实践,揭开「宏任务」(Macrotasks)与「微任务」(Microtasks)的神秘面纱✨。


🌟 什么是事件循环?

事件循环是JavaScript运行时环境(如浏览器或Node.js)中用于协调执行代码的一种机制。它负责管理和执行两种类型的任务队列:「宏任务」(Macrotasks)和「微任务」(Microtasks),以确保异步操作能够有序且高效地完成,同时保证了代码的非阻塞执行特性。

在这里插入图片描述

具体来说,事件循环的工作流程遵循以下步骤:

  1. 执行全局脚本代码:当JavaScript引擎开始执行一段脚本时,它首先会执行所有的同步代码,这些代码被视为当前「宏任务」的一部分。

  2. 检查「微任务」队列:在当前「宏任务」执行完毕后,事件循环会查看「微任务」队列。如果有待处理的「微任务」,事件循环会清空整个「微任务」队列,即连续执行队列中的所有「微任务」,直到队列为空。这一过程会在每次「宏任务」结束后重复,确保「微任务」能够迅速得到响应。

  3. 执行宏任务:在处理完当前所有「微任务」后,事件循环会从「宏任务」队列中取出下一个任务并执行。这包括像setTimeoutsetInterval的回调、I/O操作完成、UI渲染等。

  4. 重复循环:一旦当前「宏任务」执行完毕,事件循环会再次检查是否有新的「微任务」需要处理,如此反复循环,直至「宏任务」队列和「微任务」队列均为空,或者有外部中断(如用户交互)发生。

事件循环的关键在于它确保了异步操作能够插入到同步代码执行的间隙中,既不会阻碍同步代码的执行,又能及时响应各种事件和数据变化,是实现JavaScript异步编程模型的基础。


📚 「宏任务」 vs 「微任务」

JavaScript的异步编程模型中,任务被划分为两大类:宏任务(Macrotasks)微任务(Microtasks)。这两者在事件循环中的处理方式和执行时机有着本质的不同,深刻理解它们对于编写高性能和可预测性的异步代码至关重要。

「宏任务」(Macrotask)

定义与特点:

  • 「宏任务」代表了执行环境中较重的异步操作,它们由宿主环境(如浏览器或Node.js)直接控制。
  • 这些任务通常涉及I/O操作、用户交互(如点击事件)、UI渲染以及基于定时器的功能(setTimeoutsetInterval)。
  • 每次事件循环的迭代中,最多只会执行一个「宏任务」 ,执行完毕后,事件循环会检查是否存在待处理的「微任务」

常见例子:

  • DOM事件处理(如点击、加载事件)
  • setTimeoutsetInterval 的回调
  • I/O 操作(文件读写)
  • UI渲染

「微任务」(Microtask)

定义与特点:

  • 相比之下,「微任务」更为轻量级,它们通常由JavaScript引擎自身管理,用于执行一些需要快速响应的操作。
  • 「微任务」在当前「宏任务」 结束后立即执行,且在下一个「宏任务」 开始前必须全部执行完毕,这使得「微任务」具有更高的优先级。
  • 在一个「宏任务」 执行期间,可以生成多个「微任务」,这些「宏任务」会在当前「宏任务」结束后形成一个队列,并一次性执行完毕。

常见例子:

  • Promise 的回调(.then.catch.finally
  • MutationObserver 的回调
  • queueMicrotask(在某些环境中提供,作为「微任务」的直接API)
  • Node.js 中的 process.nextTick

实际应用中的注意事项

  • 性能考量:由于微任务在同一个「宏任务」内连续执行,应避免在「微任务」中执行大量计算或产生大量新的「微任务」,以防调用栈过深导致性能问题。
  • 任务调度:合理安排「宏任务」「微任务」的使用,以控制代码执行的时机和顺序,特别是在处理异步逻辑时。

理解「宏任务」「微任务」的执行模型,可以帮助开发者更好地设计异步流程,避免因执行时机不当引发的bug,提升应用的响应速度和用户体验。


🔀 执行流程概览

  1. 全局脚本开始执行:当JavaScript引擎开始运行时,它会先执行全局脚本的同步代码部分。这是每个事件循环的起点,包括变量声明、函数定义等。。
  2. 执行当前「宏任务」:在执行过程中遇到诸如setTimeoutsetIntervalI/O请求或用户交互事件(在它们实际触发时),它们的处理逻辑会被安排到未来的「宏任务」队列中,而不是立即执行。同时,如果遇到新创建的Promise并且其内部有.then.catch,这些回调会被加入「微任务」队列
  3. 执行所有可执行的「微任务」:在当前「宏任务」执行完毕后,会检查「微任务」队列(这包括了Promise回调、MutationObserver回调等),「微任务」队列中的所有任务会在这个阶段被连续执行,直到队列为空,然后再继续下一轮事件循环。
  4. 渲染页面(如果有必要):在所有「微任务」执行完成后,浏览器判断是否需要重新渲染页面。如果DOM结构、CSS样式等有变化,此时可能触发UI更新。。
  5. 检查新的「宏任务」:一旦「微任务」队列 清空且完成可能的渲染工作,事件循环会检查是否有新的「宏任务」到达(例如来自网络的响应、定时器到期),并开始执行下一个「宏任务」的同步代码部分。整个过程形成了一个循环,不断迭代,直到「宏任务」队列为空且没有新的任务加入,此时JavaScript运行环境可能会进入休眠状态,等待新的事件触发。

在这里插入图片描述


📝 代码示例

下面的代码将帮助你直观理解「宏任务」「微任务」的执行顺序:

console.log('Start'); // 1. 同步代码首先执行

setTimeout(() => {
  console.log('setTimeout'); // 4. 宏任务,最后执行
}, 0);

new Promise((resolve) => {
  console.log('Promise'); // 2. 同步代码块内的Promise立即执行
  resolve();
}).then(() => {
  console.log('Promise.then'); // 3. 微任务,在当前宏任务的末尾执行
});

// 输出顺序: Start -> Promise -> Promise.then -> setTimeout

这段代码展示了JavaScript「宏任务」(Macrotask)与「微任务」(Microtask)的执行顺序。下面是详细的解释,配合代码注释:

console.log('Start'); // 1. 同步代码首先执行
  • 同步代码执行:首先,JavaScript会执行全局脚本中的同步代码,输出"Start"。
setTimeout(() => {
  console.log('setTimeout'); // 4. 宏任务,最后执行
}, 0);
  • setTimeout 宏任务调度:接着,遇到setTimeout函数,它安排了一个「宏任务」到任务队列。虽然延迟时间设为0,但这不代表立即执行,而是意味着至少在当前脚本执行完成后,且最早在下一次事件循环开始时执行。因此,"setTimeout"的输出会晚于「微任务」
new Promise((resolve) => {
  console.log('Promise'); // 2. 同步代码块内的Promise立即执行
  resolve();
}).then(() => {
  console.log('Promise.then'); // 3. 微任务,在当前宏任务的末尾执行
});
  • Promise构造函数执行:创建并立即执行Promise构造函数中的代码,输出"Promise"。这是同步执行的一部分。

  • Promise.then 微任务调度:当Promise被解析(这里通过resolve()方法),其关联的.then方法中的回调会被加入到「微任务」队列。因为「微任务」总是在当前「宏任务」的最后执行,所以在"Promise"之后,紧接着输出"Promise.then"。

总结输出顺序: Start -> Promise -> Promise.then -> setTimeout

这清晰地表明了JavaScript运行时的事件循环机制:先执行同步代码,然后在每个「宏任务」结束前,会执行完所有的「微任务」。之后,才会继续下一个「宏任务」


🔍 深入理解「微任务」执行时机

「微任务」总是在当前「宏任务」结束后,下一个「宏任务」开始前执行。这意味着,如果你在一个「宏任务」的执行过程中不断地生成新的「微任务」,它们会继续累积,并在当前「宏任务」结束前全部执行完毕,而不是等待到下一轮事件循环。

「微任务」的执行流程特点

  1. 即时性「微任务」在当前执行栈清空后立即执行,这意味着它们不会受到后续「宏任务」的延迟影响。
  2. 连续执行:如果在执行一个「微任务」的过程中又产生了新的「微任务」,这些新产生的「微任务」不会等待当前循环结束,而是在当前「宏任务」「微任务」队列中继续累积,并在当前「宏任务」结束前依次执行完毕。这一过程会持续到「微任务」队列为空。
  3. 可能导致“任务饿死”:极端情况下,如果不断产生「微任务」而不让 JavaScript 引擎有机会处理「宏任务」,可能会导致浏览器或Node.js环境中的其他任务长时间得不到执行,表现为界面冻结或服务器响应迟缓等问题,这种情况称为“任务饿死”。

示例代码加深理解:

console.log('Start');

function createMicrotask() {
  Promise.resolve().then(() => {
    console.log('Microtask');
    createMicrotask(); // 递归创建新的微任务
  });
}

createMicrotask();

console.log('End');

在这个例子中,createMicrotask函数通过Promise的.then方法创建了一个「微任务」。该函数在被调用时会打印"Microtask",并且递归调用自身再次创建一个新的「微任务」。由于「微任务」会在当前「宏任务」结束前全部执行完毕,因此在"End"被打印之后,控制台会连续输出多行"Microtask",直到达到JavaScript引擎的调用栈限制或其他停止条件为止。这进一步说明了「微任务」的即时执行和累积效应。


💡 实践建议

1. 避免过度依赖「微任务」

  • 使用场景选择:明确区分哪些操作更适合放在「微任务」中执行,如DOM更新后的立即操作、异步操作的快速回调等。对于需要一定延迟或不急于立即执行的任务,则可以考虑放入「宏任务」队列
  • 递归与循环控制:如示例代码所示的递归「微任务」调用,务必设置合理的退出条件,防止无限递归导致堆栈溢出。

2. 控制异步逻辑复杂度

  • 模块化和分层:将复杂的异步逻辑拆分为多个小的、职责单一的函数或模块,每个部分专注于处理一类任务,这样既便于阅读也易于维护。
  • 使用Async/Await:在支持的环境中,使用async/await语法糖可以极大地简化异步代码的编写,使其更像同步代码,提高可读性。

3. 注意异步操作对UI更新的影响

  • UI更新策略:虽然「微任务」先于UI渲染执行,但频繁的DOM操作或样式更改仍可能引起页面重排和重绘,影响性能。尽量批量处理DOM修改,或者利用requestAnimationFrame来协调UI更新,确保动画流畅。
  • 用户交互响应:确保关键的用户交互逻辑(如点击事件处理)能够迅速响应,避免在这些处理中嵌套过多「微任务」,以免阻塞UI线程,造成应用无响应的感觉。

4. 性能监控与调试

  • 工具利用:利用Chrome DevTools等开发者工具的Performance面板监控应用运行时的「宏任务」「微任务」调度情况,识别潜在的性能瓶颈。
  • 压力测试:在不同的设备和网络环境下进行压力测试,模拟高负载场景,检查「微任务」是否导致了不可接受的延迟或性能下降。

遵循这些建议,可以帮助开发者有效地平衡异步处理的需求与性能优化,确保应用程序的健壮性和用户体验。


🎖 总结

  • JavaScript事件循环是其异步编程模型的基础,它确保了非阻塞IO操作和高并发能力,让浏览器或Node.js环境下的JavaScript应用能高效运行。

  • 宏任务(Macrotasks),如setTimeoutsetIntervalI/OUI rendering等,构成了事件循环的主干,它们在每次循环的开始执行,控制着程序的主要流程。

  • 微任务(Microtasks),例如Promise的回调(then/catch/finally)、MutationObserverprocess.nextTick(在Node.js中)等,紧随当前「宏任务」之后执行,且在下一次「宏任务」开始前尽可能多地清空「微任务」队列,这使得「微任务」适用于需要立即响应的场景。

理解并正确利用「宏任务」「微任务」的执行机制,对于避免常见的异步陷阱、优化应用性能、以及实现流畅的用户体验至关重要。无论是处理复杂的异步逻辑、提升UI交互的即时反馈,还是维护代码的可读性和可维护性,掌握这一核心知识都能让你的JavaScript编程技能更上一层楼。

通过本文,希望你已经掌握了JavaScript事件循环的核心概念,理解了「宏任务」「微任务」的区别及其执行顺序。记住,掌握事件循环机制对于编写高效、可预测的异步JavaScript代码至关重要。现在,你已经具备了驾驭JavaScript异步魔法的能力,去创造更流畅的用户界面和强大的Web应用吧!🌈


🔗 相关链接

  • JavaScript 中的 Class 类
  • JavaScript中call、apply与bind的区别
  • JavaScript 垃圾回收机制深度解析:内存管理的艺术

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1663927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【挑战30天首通《谷粒商城》】-【第一天】03、简介-分布式基础概念

文章目录 课程介绍 ( 本章了解即可,可以略过)1、微服务简而言之: 2、集群&分布式&节点2.1、定义2.2、示例 3、远程调用4、负载均衡常见的负裁均衡算法: 5、服务注册/发现&注册中心6、配置中心7、服务熔断&服务降级7.1、服务熔断7.2、服务降级 8、AP…

QT作业5

1、聊天室 服务器端 //头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QListWidget> #include <QMessageBox> #include <QDebug> #includ…

C语言(指针)4

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

我必须要吹一波MATLAB 2024a,太牛逼了!|福利:附安装教程及下载地址

最近逛MATLAB官网&#xff0c;发现MATLAB 2024a版本已经Pre-release了&#xff0c;翻了下release note&#xff0c;不得不感叹&#xff0c;实在是太强了&#xff01; 这次重点更新了四个工具箱&#xff1a; Computer Vision Toolbox Deep Learning Toolbox Instrument Contro…

1.基于python的单细胞数据预处理-降维可视化

目录 降维的背景PCAt-sneUMAP检查质量控制中的指标 参考&#xff1a; [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 降维的背景 虽然特征选择已经减少了维数&#xff0c;但为了可视化&#xff0…

InputStream,OutputStream的用法以及相应的案例

1. 文件系统的操作&#xff1a;File类。 2. 文件内容的操作&#xff1a;Stream流。 字符流&#xff1a;IntputStream &#xff0c; OutputStream。 字节流&#xff1a;read &#xff0c; write。 InputStream&#xff0c;OutputStream InputStream和OutputStream都不能被实例…

Web数字孪生引擎

Web数字孪生引擎是指用于在Web上创建和运行数字孪生的软件平台。它们通常提供一组API和工具&#xff0c;用于连接到实时数据源、可视化数据并创建交互式体验。Web数字孪生引擎被广泛应用于各种应用&#xff0c;例如工业物联网、智能建筑、城市管理和公共安全等。北京木奇移动技…

Linux 安裝 rpm包

下载 地址&#xff1a;https://developer.aliyun.com/packageSearch 安装 rpm -ivh lsof-4.87-6.el7.x86_64.rpmlsof -Ki|awk {print $2}|sort|uniq -c|sort -nr|head lsof | wc -l

上位机图像处理和嵌入式模块部署(树莓派4b和电源供给)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面&#xff0c;我们说过pc电脑和嵌入式设备&#xff0c;两者都可以实现相同的软件功能。但是和pc相比较&#xff0c;嵌入式设备不仅价格更便宜&a…

RabbitMQ (windows) 安装

大家好我是苏麟 , 今天安装一下 RabbitMQ . 官网 : RabbitMQ: One broker to queue them all | RabbitMQ 1.点击 Getting Started 2. 点击 Docs 3.点击 Install And Upgrade 4.点击 installation via Chocolatory 5. 直接下载安装包 RabbitMQ 下好了先放在一遍 RabbitMQ 需要 E…

前端框架-echarts

Echarts 项目中要使用到echarts框架&#xff0c;从零开始在csdn上记笔记。 这是一个基础的代码&#xff0c;小白入门看一下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" co…

新手做抖音小店必须了解的回款周期问题!这样做,一步快速结算

哈喽~我是电商月月 我们做抖音小店&#xff0c;在商品上架出单后&#xff0c;商家需要到商品的上家拍单&#xff0c;由上家发货给顾客&#xff0c;顾客收到货后&#xff0c;周期一到才能赚取到商品的差价&#xff0c;这个时间大概在7~15天之内&#xff0c;想准时发货不超时违规…

八款免费好用的3D建模AI工具,让你的设计更简单!

随着人工智能和大语言模型的不断发展&#xff0c;AI工具正逐渐渗透到3D建模领域中。传统上&#xff0c;3D建模师需使用如3ds Max、Maya等这类复杂的3D建模软件&#xff0c;投入大量的时间与精力来创作精细的模型。然而&#xff0c;有了AI工具的辅助&#xff0c;设计过程不仅对专…

激光测径仪在胶管生产中扮演着什么角色?

关键词&#xff1a;激光测径仪,胶管,胶管测径仪,在线测径仪 胶管生产的基本工序为混炼胶加工、帘布及帆布加工、胶管成型、硫化等。不同结构及不同骨架的胶管&#xff0c;其骨架层的加工方法及胶管成型设备各异。 全胶胶管因不含骨架层&#xff0c;只需使用压出机压出胶管即可&…

AWS云优化:实现性能和成本的最佳平衡

随着企业数字化转型的加速&#xff0c;对云计算平台的需求也不断增长。AWS作为云计算行业的领导者之一&#xff0c;提供了广泛的云服务和解决方案&#xff0c;帮助企业实现业务的创新和发展。在AWS云上部署应用程序和服务后&#xff0c;对其进行优化是至关重要的&#xff0c;以…

【官方下载】Android 开发环境之 java 11 下载地址和下载操作步骤

作者介绍&#xff1a; 百度资深Android工程师T6&#xff0c;在百度任职7年半。 目前&#xff1a;成立赵小灰代码工作室&#xff0c;欢迎大家找我交流Android、微信小程序、鸿蒙项目。文章底部&#xff0c;csdn有为我插入微信的联络方式&#xff0c;欢迎大家联络我。 一&#x…

TikTok机房ip好还是住宅ip好?

住宅ip比较好&#xff0c;机房数据中心IP高效、低价&#xff0c;所以使用的人多且用处复杂&#xff0c;这类ip极大可能存在滥用的黑历史&#xff0c;通过此类ip访问tiktok&#xff0c;被禁止的可能性更高&#xff0c;更容易被拉入黑名单。所以我们推荐tiktok独享原生ip搭建节点…

计算机组成结构—指令和指令格式

目录 一、指令的基本格式 二、指令字长 1. 定长指令字结构 2.变长指令字结构 三、地址码 1.四地址指令 2.三地址指令 3.二地址指令 4.一地址指令 5. 零地址指令 四、操作码 1. 定长操作码指令格式 2. 扩展操作码指令格式 五、指令的操作数类型和操作类型 1. 操作…

openai 开源模型Whisper语音转文本模型下载使用

Whisper Whisper 是一种通用语音识别模型。它是在大量不同音频数据集上进行训练的,也是一个多任务模型,可以执行多语言语音识别、语音翻译和语言识别。官方地址 https://github.com/openai/whisper 方法 一个Transformer序列到序列模型被训练在多种语音处理任务上,包括多语…

mac 讨厌百度网盘怎么办

一、别拦我 首先请允许我泄个愤&#xff0c;tmd百度网盘下个1g的文件下载速度竟然超不过200k&#xff0c;只要不放在所有已打开软件的最前面&#xff0c;它就给你降到10k以内&#xff0c;关键是你慢就慢了&#xff0c;我也不是很着急&#xff0c;关键是你日常下载失败并且总是…