深入理解 JavaScript 事件循环机制:单线程中的异步处理核心
深入理解 JavaScript 事件循环机制:单线程中的异步处理核心
JavaScript 是一门单线程的编程语言,也就是说它在同一时间只能执行一个任务。然而,现代 Web 应用经常需要处理大量的异步操作,如用户输入、网络请求、定时器等。为了确保在这些操作期间应用的流畅运行,JavaScript 引入了事件循环机制(Event Loop),它使得单线程也能高效地处理异步任务。
本文将深入分析 JavaScript 的事件循环机制及其核心组件,帮助你更好地理解和使用这一强大的异步处理工具。
事件循环机制的核心组件
1. 执行栈(Call Stack)
执行栈是一个 LIFO(后进先出)结构,用来管理所有的同步任务。当函数被调用时,它会被推入执行栈顶,函数执行完毕后才会从栈中弹出。JavaScript 在单线程中执行代码的顺序是严格按照执行栈来完成的。
关键点:由于 JavaScript 是单线程的,执行栈中的同步任务会阻塞其他任务的执行。因此,当执行栈上有耗时的任务时,会导致 UI 渲染、用户输入等操作的延迟。为了解决这个问题,JavaScript 借助事件循环机制来处理异步任务。
2. 消息队列(Message Queue)
消息队列是一个 FIFO(先进先出)结构,用于存放待处理的异步任务。这些任务通常包括宏任务(Macro Task),例如 setTimeout、setInterval、网络请求的回调等。
任务调度:当执行栈中的所有同步代码执行完毕后,事件循环会从消息队列中取出任务,按顺序将它们放入执行栈中执行。消息队列的存在保证了异步任务不会阻塞同步任务。
3. 微任务队列(Microtask Queue)
微任务队列存储优先级比宏任务更高的 轻量级异步任务 ,通常用于处理一些短小、紧急的任务。微任务队列中的任务包括 Promise 的回调、MutationObserver 和 process.nextTick(Node.js)。
优先级:每个宏任务执行完毕后,事件循环会立即处理微任务队列中的所有任务。在处理完微任务队列中的任务之前,事件循环不会继续执行下一个宏任务。
4. Web APIs 和 Node.js APIs
虽然 JavaScript 是单线程的,但浏览器和 Node.js 提供的底层 Web APIs 或 Node.js 系统 APIs(如定时器、网络请求等)可以借助多线程机制处理异步任务。当这些任务完成时,它们的回调函数会被推入消息队列等待执行。
事件循环的执行流程
JavaScript 的事件循环遵循一个简单但高效的流程:
-
执行同步代码:事件循环首先会执行执行栈中的同步任务。同步任务依次入栈、执行、出栈,直到栈为空。
-
处理微任务:执行栈清空后,事件循环会优先处理微任务队列中的任务。如果微任务在执行过程中产生了新的微任务,这些任务也会立即被执行,直到微任务队列为空。
-
处理宏任务:当微任务队列清空后,事件循环会从消息队列中取出 一个宏任务 ,将其放入执行栈中执行。宏任务执行完毕后,事件循环再次处理微任务队列。
-
重复循环:事件循环会不断重复上述步骤,保证异步任务与同步任务的协调执行。
宏任务与微任务
宏任务(Macro Task)
宏任务是相对较大的异步任务,每个事件循环中只能执行一个宏任务。常见的宏任务包括:
setTimeout、setInterval:用于设置定时器,回调函数会在指定时间后被推入消息队列。- I/O 操作:如文件读取、网络请求等任务的回调。
- 事件处理器:例如
click或keydown等事件的回调函数。 - UI 渲染任务:浏览器中的重排(Reflow)和重绘(Repaint)。
setImmediate(Node.js 环境中): 当前事件循环结束后立即执行的回调。requestAnimationFrame:用于在浏览器中下一帧渲染之前执行的回调。
微任务(Microtask)
微任务优先级高于宏任务,在每次宏任务执行结束后会优先处理。常见的微任务包括:
Promise.then,catch,finally:Promise 的回调总是在当前事件循环的微任务队列中调度执行。MutationObserver:DOM 发生变化时的回调。process.nextTick(Node.js):一种特殊的微任务,优先级甚至高于Promise。queueMicrotask:显式将回调函数加入微任务队列。
宏任务与微任务的执行顺序示例
通过以下代码示例,我们可以理解宏任务与微任务的执行顺序:
console.log('Start');setTimeout(() => {console.log('Timeout 1');
}, 0);Promise.resolve().then(() => {console.log('Promise 1');
}).then(() => {console.log('Promise 2');
});console.log('End');
执行过程:
console.log('Start')和console.log('End')是同步任务,立即执行。setTimeout的回调函数被推入消息队列,等待宏任务调度。Promise.resolve()生成的.then()回调函数被推入微任务队列。- 同步任务执行完毕后,事件循环会先处理微任务队列,依次输出
Promise 1和Promise 2。 - 最后,事件循环会从消息队列中取出
setTimeout的回调,输出Timeout 1。
最终输出顺序为:
Start
End
Promise 1
Promise 2
Timeout 1

宏任务与微任务的列表总结
宏任务:
setTimeoutsetIntervalsetImmediate(Node.js)requestAnimationFrame- I/O 操作
- 事件处理器(如
click、keydown等) postMessageMessageChannel- UI 渲染任务(如重排和重绘)
微任务:
Promise.then,catch,finallyMutationObserverprocess.nextTick(Node.js)queueMicrotaskAsync/Await
实际应用场景
1. 异步操作的处理
事件循环机制在处理异步操作时显得尤为重要。无论是网络请求、用户交互,还是定时器的执行,它们的回调函数都不会立即执行,而是通过事件循环的调度机制有序执行。这使得主线程不会因等待异步任务的完成而阻塞。
2. 性能优化
开发者可以利用微任务的优先级特性来优化代码的执行顺序。通过 Promise 或 queueMicrotask,可以将需要优先处理的任务放入微任务队列,确保它们在当前事件循环中尽快执行。
3. 避免阻塞主线程
事件循环机制通过将耗时任务交由 Web APIs 或 Node.js APIs 处理,避免了同步任务阻塞主线程。这在处理大量用户交互或后台数据处理时至关重要。
总结
JavaScript 的事件循环机制使得单线程环境下也能高效处理异步任务。通过执行栈、消息队列、微任务队列的协调工作,JavaScript 在不阻塞主线程的情况下完成各种异步操作。理解事件循环的工作原理,有助于开发者编写出更加高效、响应迅速的 Web 应用。
掌握宏任务和微任务的优先级以及事件循环的调度逻辑,是优化异步操作和改善用户体验的关键。
相关文章:
深入理解 JavaScript 事件循环机制:单线程中的异步处理核心
深入理解 JavaScript 事件循环机制:单线程中的异步处理核心 JavaScript 是一门单线程的编程语言,也就是说它在同一时间只能执行一个任务。然而,现代 Web 应用经常需要处理大量的异步操作,如用户输入、网络请求、定时器等。为了确…...
Stream流的终结方法(二)——collect
1.Stream流的终结方法 2. collect方法 collect方法用于收集流中的数据放到集合中去,可以将流中的数据放到List,Set,Map集合中 2.1 将流中的数据收集到List集合中 package com.njau.d10_my_stream;import java.util.*; import java.util.f…...
【C语言系统编程】【第一部分:操作系统知识】1.1.操作系统原理
第一部分:操作系统知识 1.1 操作系统原理 1.1.1 进程管理 1.1.1.1 进程的概念与生命周期 进程是程序在计算机中的一次执行实例,包括了程序的代码、数据、以及运行的上下文环境。进程管理是操作系统的核心任务之一。 作用:管理所有执行中…...
基于Java+VUE+echarts大数据智能道路交通信息统计分析管理系统
大数据智能交通管理系统是一种基于Web的系统架构,通过浏览器/服务器(B/S)模式实现对城市交通数据的高效管理和智能化处理。该系统旨在通过集成各类交通数据,包括但不限于车辆信息、行驶记录、违章情况等,来提升城市管理…...
leetcode-42. 接雨水 单调栈
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表…...
ThinkPHP和PHP的区别
文章目录 ThinkPHP和PHP的区别一、引言二、PHP简介1、第一步1.1、示例代码 三、ThinkPHP简介2、第二步2.1、特点2.2、示例代码 四、总结 ThinkPHP和PHP的区别 一、引言 在Web开发领域,PHP是一种广泛使用的开源脚本语言,而ThinkPHP则是一个基于PHP的MVC…...
clientWidth,offsetWidth,scrollHeight
clientWidth: offsetWidth: scrollHeight:...
SVN版本回退
SVN 版本回退三种方法: Update item to this version 假设我们的项目文件一共有8个版本,它版本号分别是1,2,3,4,5,6,7,8。 这个选项的作用是将文件版本更新到对应所选的…...
IDEA关联Tomcat
一、Tomcat服务器 web服务器,就是运行web项目的容器 即运行java代码的一个容器 webapp(web应用程序) --> 就是我们写的javaweb项目 Tomcat 是Apache 软件基金会(Apache Software Foundation)下的一个核心项目,免费开源、并支持Servlet 和J…...
MongoDB mongoose 的 save、insert 和 create 方法的比较
目录 save 方法 insert 方法 create 方法 使用会话和事务 总结 在本文中,我们将介绍 MongoDB 中使用 mongoose 操作 数据库时的三种常见方法:save、insert 和 create。这些方法可以用于将数据存储到 MongoDB 数据库中,并且在一定程度上具…...
Maven安装使用
说明:Maven是Apache旗下的一个开源项目,是一款用于管理和构建java项目的工具。一般来说,它帮助我们管理依赖、构建项目。本文介绍在Windows系统下安装Maven。 下载&安装&验证 下载 首先,在Maven官网(https:…...
微信第三方开放平台接入本地消息事件接口报错问题java.security.InvalidKeyException: Illegal key size
先看报错: java.security.InvalidKeyException: Illegal key sizeat javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)at javax.crypto.Cipher.implInit(Cipher.java:805)at javax.crypto.Cipher.chooseProvider(Cipher.java:864)at javax.crypto.Cipher.in…...
如何只修改obsidian图片链接为markdown
如何只修改obsidian图片链接为markdown 前言插件配置 使用注意 前言 适合有一定了解obsidian用法和插件市场,还有相对路径的人 插件 在obsidian插件市场搜索—开梯子 配置 首先使用ctrlp打开命令面板,也可以在左侧通过图标打开命令面板,…...
AI不可尽信
看到某项目有类似这样的一段代码 leaves : make([]int, 10) leaves leaves[:0]没理解这样的连续两行,有何作用? 初始化一个长度和容量都为10的切片,接着把切片长度设置为0 即如下demo: (在线地址) package mainimport "fmt"func main() {leaves : make([]int, 1…...
[C++]使用纯opencv部署yolov11旋转框目标检测
【官方框架地址】 GitHub - ultralytics/ultralytics: Ultralytics YOLO11 🚀 【算法介绍】 YOLOv11是一种先进的对象检测算法,它通过单个神经网络实现了快速的物体检测。其中,旋转框检测是YOLOv11的一项重要特性,它可以有效地检…...
Python入门--函数
目录 1. 函数介绍 2. 函数的定义 3. 函数的参数 4. 函数的返回值 5. 函数说明文档 6. 函数的嵌套调用 7. 函数的作用域 (1). 局部变量 (2). 全局变量 (3). global关键字 1. 函数介绍 函数:是组织好的,可重复使用的,用来实现特定功能…...
winFrom界面无法打开
🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&am…...
【Linux】Ubuntu20.04上使用RabbitVCS的图形化SVN
文章目录 1、RabbitVCS1.1、RabbitVCS 介绍1.2、RabbitVCS 主要功能1.3、Ubuntu下 TortoiseSVN 替代者 2、安装2.1、命令安装2.2、安装使用2.3、使用权限 3、解决SVN无法保存密码问题3.1、问题描述3.2、解决方法 1、RabbitVCS 1.1、RabbitVCS 介绍 它是一款Linux系统下的图形…...
DMA直接存储器存取
参考视频:[8-1] DMA直接存储器存取_哔哩哔哩_bilibili DMA简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源 12个独立可…...
java计算机毕设课设—坦克大战游戏
这是什么系统? 坦克大战游戏是一款以坦克为主题的射击游戏,旨在为玩家提供一个刺激、有趣的游戏体验。该游戏不仅拥有丰富的功能,还注重玩家的互动体验。此系统是使用Java语言实现坦克大战游戏程序,玩家通过连接访问进入游戏&…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
