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

在这里插入图片描述

🔥 个人主页:空白诗

在这里插入图片描述

文章目录

  • 🎉 引言
    • 🌟 什么是事件循环?
    • 📚 「宏任务」 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 垃圾回收机制深度解析:内存管理的艺术

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

算法学习笔记(2)-前缀和

##前缀和 指的是某序列的前n项和,在数学上我们可以理解称为数列的前n项和。前缀和是一种预处理,用于降低查询的时间复杂度。 ##一维前缀和 有一个一维数组x和该数组的前缀和数组y,则x和y具有以下关系: #python代码示例 #关系&am…

美国成立AI安全委员会:马斯克与扎克伯格被排除,权力游戏引热议!

2024年人工智能(AI)安全与监管的复杂性及其背后的权力、利益和道德问题,首先介绍了美国国土安全部成立的AI安全与安全委员会,以及两位人工智能领域巨头埃隆马斯克和马克扎克伯格被排除在外的现实,从权力和利益的角度分…

Vulnhub靶机随笔-Hacksudo_Aliens

Vulnhub靶机Hacksudo_Aliens详解 攻击机Kali IP:192.168.3.44 靶机 IP:未知 系统:未知 A.信息收集 扫描靶机存活性 确定IP地址 1.命令:arp-scan -l 扫描靶机开放端口及其服务版本信息 2.命令 nmap -A -p- -sV 靶机IP地址 靶机开放三个端口,22ssh端口,80web端…

Paper Digest | 基于原型学习的实体图谱预训练跨域推荐框架

欢迎大家在 GitHub 上 Star 我们: 分布式全链路因果学习系统 OpenASCE: https://github.com/Open-All-Scale-Causal-Engine/OpenASCE 大模型驱动的知识图谱 OpenSPG: https://github.com/OpenSPG/openspg 大规模图学习系统 OpenAGL: https://github.com/TuGraph-…

【git】通过JetNrains IDE对git的操作

应该适用于所有jetbrains产品。 一、拉取(pull)代码 上方工具栏-Git-克隆。然后填写git地址与本地存放地址。 二、搁置 修改代码后搁置代码(不提交,但是也不撤销已修改的代码,把它暂存起来)。 界面的左上角。1->2->3。完事就可以写换到其他分支肆意妄为^^。 三…

Vue项目npm install certificate has expired报错解决方法

1.Vue项目 npm install 安装依赖突然报错: npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/zrender/download/zrender-4.3.0.tgz failed, reason: certificate has expired npm ERR! A com…

数据是形成新质生产力的优质生产要素

在数字经济背景下,新质生产力以科技创新推动产业创新为要义,以大幅提升全要素生产率为目标,重在加强人工智能、大数据、物联网、工业互联网等数字技术的融合应用,以数据开发利用为引擎促使生产要素实现创新性配置,催生…

探针流量检测与回溯分析,解密AnaTraf网络流量分析仪的神奇魅力

目录 导言 概述 流量检测探针 流量回溯分析 网络故障解决案例 了解更多 导言 在当今互联网时代,网络性能监测与诊断成为企业发展的关键。为了解决网络故障和提升网络性能,AnaTraf网络流量分析仪应运而生。本文将详细介绍AnaTraf的功能和优势&#…

一些Webshell-Bypass的思路

—— 经过这一段时间的研究,针对webshell-Bypass我也有了一些自己的技巧,于是决定写下这篇文章,阅读前提是需要有一点PHP的语言基础。 在讲解代码之前,需要简单了解一下不同查杀平台webshell查杀的查杀原理。对于一些较传统的We…

无限集中的最小数字

题目链接 无限集中的最小数字 题目描述 注意点 1 < num < 1000 解答思路 由题意得&#xff0c;可以理解为最初集合中有1~1000之间的所有数字&#xff0c;如果集合中存在数字&#xff0c;则添加时不会有任何操作&#xff1b;在移除集合中的元素时&#xff0c;会按顺序…

软件体系结构总结

文章目录 一、软件体系结构概述1.1 基本概念1.1.1 背景1.1.2 定义1.1.3 系统1.1.3.1 定义1.1.3.2 特性1.1.3.3 系统的体系结构 1.1.4 软件设计的层次性1.1.5 体系结构的类别&#xff08;类型&#xff09;1.1.6 重要性&#xff08;意义&#xff09; 1.2 模块及其设计1.2.1 定义1…

正点原子Linux学习笔记(九)在 LCD 上显示字符

在 LCD 上显示字符 23.1 原始方式&#xff1a;取模显示字符23.2 freetype 简介23.3 freetype 移植下载 FreeType 源码交叉编译 FreeType 源码安装目录下的文件移植到开发板 23.4 freetype 库的使用初始化 FreeType 库加载 face 对象设置字体大小加载字形图像 23.5 示例代码 前面…

国产根SSL证书,验证签发数据不出境

在探讨SSL证书数据是否可能“出境”的问题之前&#xff0c;我们需要先理解几个基本概念&#xff1a;什么是SSL证书、数据传输的基本流程&#xff0c;以及“出境”在此语境下的含义。本文旨在以科普的方式&#xff0c;清晰地解析这一主题&#xff0c;帮助读者建立起对SSL证书及其…

upload组件封装,支持拖拽文件上传

一、组件封装需要注意什么? 组件化思想:组件应该是独立的、可复用的部件,应该遵循单一职责原则,将组件的功能划分得尽可能细致。 API 设计:组件的 API 设计要合理,要考虑到组件的可定制性和易用性。应该尽可能的提供必要的配置项和事件回调,同时避免提供过多的 API,导…

【启明智显分享】国产自主HMI核心板Model3

Model3是一款高性能的工业级HMI&#xff08;人机界面&#xff09;核心板&#xff0c;也是一款纯国产HMI方案&#xff0c;工业级标准&#xff0c;稳定、可靠&#xff1b; 工业级HMI芯片–Model3 纯国产HMI方案 Model3核心板&#xff0c;具有2D加速&#xff0c;PNG解码&…

高效电源测试设备助力自动化测试和数据分析

在当今电子产品的研发和生产过程中&#xff0c;电源测试设备的重要性不言而喻。一款优秀的电源测试设备能够显著提升测试效率&#xff0c;确保电源模块的性能达到设计要求。 纳米软件NSAT-8000电源测试系统是一款自动化电源测试设备&#xff0c;在测试电源模块时&#xff0c;通…

ESP32 + ST7789 LCD

1、准备 ESP32 单片机开发板 ST7789 LCD 模块&#xff08;240 * 320 像素&#xff09; 杜邦线 2、接线 LCD功能ESP32VCC 供电电压正极 3.3V 、 5V GND 供电电压负极 GNDIDN / MOSI SPI 接口数据 引脚 23CLK 串行接口时钟信号 18CS 芯片选择引脚&#xff1b;低电平有效 5DC 显…

监控员工上网用什么软件,4款优秀的上网行为监控软件优选

很多员工都会对工作有懈怠心理。 在数字化办公环境中&#xff0c;员工的上网行为直接影响着工作效率、信息安全与合规运营。 为确保企业资源合理利用、防止潜在风险&#xff0c;上网行为监控软件成为企业管理的重要辅助工具。 本文将为您推荐五款优秀的上网行为监控软件&#…

基于STC12C5A60S2系列1T 8051单片机实现一主单片机给一从单片机发送数据的串口通信功能

基于STC12C5A60S2系列1T 8051单片机实现一主单片机给一从单片机发送数据的串口通信功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串口通信的特殊功能寄存…