JS的事件循环(Event Loop)

news2025/5/20 0:30:49

JS的事件循环

本文是我自己对事件循环的理解和总结,不会有太多的理论知识,已经有太多文章写过了,自己搜索下就能找到很多;
同时,文章中的观点仅是本人自己理解,比较白话,不用太较真啊!

事件循环是什么?

事件是什么?

事件是特定情况下触发的操作。这里的操作是用函数封装起来的,所以,事件就是在特定情况下触发的函数
例如:

  • dom元素相关事件,在用户单击页面上的按钮或页面加载完成时触发;
  • 定时器中,在特定时间间隔后触发其绑定函数;
  • ajax请求中,在请求响应时,才会触发回调函数;
  • promise中,在其内代码有了结果后,再触发 then 函数;

上面的这些情况的回调函数,都是在特定情况下触发的函数,所以都可以称为事件

特定情况触发的函数

注意,这里指的是“特定情况”,而不是按代码顺序执行时触发的,也就不是同步触发的,而是异步触发的。
所以,事件是异步触发的。

事件循环是什么?

事物循环,顾名思义,就是事件的循环触发。没错,事件循环的核心就是“事件的循环触发”;
同时事件循环还包括,这些异步操作的触发,回调函数的收集以及回调函数的触发等一系列操作。这些操作其实贯穿整个JS代码的运行
综上所述:事件循环就是JS代码的运行机制
另:
异步操作的触发, 其实是 同步执行的。
而回调函数的触发,是异步执行的

事件循环是如何实现的?

JS是单线程的

JS是单线程的,所以浏览器只会分配一个线程用来解析执行JS代码,同时所有的JS代码也只能在这一个线程中执行。此线程为JS引擎线程。
因为是单线程的,所以代码是同步执行的,也就是按代码顺序执行的,只有前一个任务执行完成,才能执行下一个任务。
那么如果实现在特定情况下触发函数呢?这就需要用于事件循环了

JS如何实现异步的

1)JS是解释型语言。所以JS是需要在宿主环境中才能运行的。而宿主环境是多线程的。
2)JS是单线程语言。所以宿主环境分配一个线程来解析运行JS代码。
3)宿主环境再利用其他线程来辅助完成其他的一些异步功能。同时创建相关任务队列,用来存储当异步功能完成后要调用的回调函数。
4)最后再循环将这些回调函数放入到JS引擎中的执行栈中,进行调用。

所以:JS的异步功能,是宿主环境的各线程相关联合实现的。而这整个过程可以称为事件循环

不同的宿主环境对JS的事件循环的实现是不同的

JS引擎运行JS代码涉及的几个概念

  • 执行栈
  • 执行上下文

宿主环境实现事件循环涉及的几个概念

  • 事件注册表 event table
  • 任务队列 task queue
    • 微任务队列
    • 宏任务队列

任务分类

  • 同步任务
  • 异步任务
    • 宏任务(在ES5版本中,异步任务只有宏任务,都放在任务队列中)
    • 微任务(在ES6版本后,异步任务中添加了微任务,此后异步任务就有了两种)

JS中的任务指的是什么?
JS中的任务通常指的是需要通过代码完成的操作或功能。这些任务可以是简单的计划操作,也可以是复杂的功能;可以是一行代码,也可以是一个函。

浏览器环境中的“事件循环”实现

异步任务中如何产生宏任务/微任务?

  • 宏任务
    • 全局作用域(script整体代码) :可以将整个script代码看成一个宏任务。算默认宏任务,在执行此任务时,再触发其他异步任务,并注册事件信息。
    • setTimeout
    • setInterval
  • 微任务
    • promise 的 then 方法
    • async 方法中,await 语句行后面的代码
    • window.queueMicroTask(fn) 的 fn 方法 中
    • new window.MutationObserve(fn) 的方法中

个人总结的执行顺序图解法:

图解法中的几条规则:

  • JS一开始是执行全局同步代码,后面再开始异步代码执行;但在“事件循环”中,可以将全局同步代码看作成一个默认执行的宏任务。所以在执行图中,同步代码作为宏任务列表的第一个宏任务。
  • 所有的宏任务都是独立,隔离的,不存在上下级关系,只有进入宏任务列表的先后关系。所以构建了一个宏任务列表,宏任务队列为事件循环的第一层循环。
  • 宏任务中的同步任务和微任务之间存在上下级关系,根据这些关系可以构建一棵以宏任务为根节点的任务树。不需要关系宏任务,它会添加到宏任务列表中

图解法评分分两个步骤:

  1. 构建执行图
    • 创建一个宏任务列表;此列表为事件循环的第一层循环
      • “script全文宏任务”作为宏任务队列的第一个任务,也是默认执行的宏任务(其实就是最外层的同步代码),可命名为 h0 。
      • 在执行同步代码时,若遇到宏任务,则在列表的后面假加任务,可依次命名为 h1, h2, … , hn。
    • 创建宏任务的下级树;将宏任务中的同步任务和微任务以上下级关系构建一棵任务树。
      • 以宏任务为任务树的根节点。
      • 将宏任务中所有的同步任务的输出写在此节点上,可用 t 标识了点。如:t : 1,3,4
      • 若遇到微任务,则新建一个微任务节点,以“w+次序“标识节点名;
        • 若微任务中只有同步任务或只有一个微任务,则可将输出直接写上,如:w0 : 5,6
        • 若同时存在同步/微任务,可分别创建节点,并写上输出。
        • 若微任务中还有下级微任务,则可继续向下创建节点。
    • 若有多个宏任务,则重复上一步骤。(注意:在之前代码的执行过程中,可添加宏任务)

2)根据任务节点图得出最终输出;

  • 根据宏任务列表自上而上,从默认任务开始;
  • 从宏根节点开始,向下级记录输出,每层节点中自上向下记录输出。
  • 一棵宏任务树记录完成后,再向后开始新的宏任务树的输出记录
  • 最终得出整段代码的执行顺序输出。

先上几个示例-用图解法

一、示例1

	// 默认宏任务 :h0 ----- 同步任务
	console.log("0");
	setTimeout(function() {  // 宏任务1 : h1
	  console.log("1");     
	  new Promise(function(resolve, reject) {
	    console.log("2");  
	    resolve();
	  }).then(() => {
	    console.log("3");  
	  });
	}, 0);
	
	new Promise(function(resolve, reject) {
	  console.log("4");  // 同步任务
	  resolve();
	}).then(() => {
	  console.log("5");  // 微任务
	});
	console.log("6");   // 同步任务
	// 输出:0  4  6  5  1  2  3

图解
在这里插入图片描述
图解说明:
1)h0, h1 组成宏任务列表,若还有宏任务,则在下面添加节点
2)以 “t:” 为前缀的节点中,冒号后面的为同步任务输出。
3)以 “w:” 为前缀的节点中,冒号后面的为微任务输出。这里只有一个任务,所以就没有加序号了。
4)最后输出:先算出每个宏任务输出,然后再将每个宏任务输出按先后累加起来。

示例2

  // h0 - 最外层同步任务
  console.log(0)   
      
  // h1   
  setTimeout(() => {      
  	// h3     
    setTimeout(()=>{console.log(6)},0)    
    console.log(1) //
    var p2 = new Promise((n1, n2) => {
      n1(1000)
    })
    p2.then(()=>{console.log(7)}) //
  }, 0)
  
  // h2
  setTimeout(() => {
  	// h4
    setTimeout(() => {console.log(2)}, 200) //
    var p3 = new Promise((n1, n2) => {
      n1(1000)
    })
    p3.then(()=>{console.log(8)})//
    console.log(2)//
  }, 0)

  var p1 = new Promise((n1, n2) => {
    n1(1000)
  })

  p1.then(() => {console.log(3)})             //

  console.log(5)       //
  // 输出:4  5  3  1  7  2  8  6  2 

图解-执行顺序
在这里插入图片描述
示例3

    setTimeout(() => {    // ----------- h1
		console.log(0);
	});
	new Promise(resolve => {
		console.log(1);
		setTimeout(() => {   // ----------- h2
			resolve(); //这里!!!!!!!
			var p1=new Promise((n1,n2)=>{n1(20)})
			p1.then(() => console.log(2));
			console.log(3);
			setTimeout(()=>{console.log(9)},0)   // ----------- h3
		});
		new Promise((n1,n2)=>{n1(20)}).then(() => console.log(4));
	}).then(() => { //这里的then函数要等resolve()执行后才能执行  一点注意!!!
		console.log(5);
		var p2=new Promise((n1,n2)=>{n1(20)})
		p2.then(() => console.log(8));
		setTimeout(() => console.log(6));   // ----------- h4
	});
	console.log(7);
	// 输出: 1  7  4  0  3  5  2  8  9  6

在这里插入图片描述
示例4

  console.log(1) 
  setTimeout(() => { 
    setTimeout(()=>{console.log(2)},0) 
    console.log(3) 
    var p2 = new Promise((n1, n2) => {
      n1(1000)
    })
    p2.then(()=>{console.log(4)}) 
  }, 0)
  setTimeout(() => {
    setTimeout(() => {console.log(5)}, 200) 
    var p3 = new Promise((n1, n2) => {
      n1(1000)
    })
    p3.then(()=>{console.log(6)})
    console.log(7)
  }, 0)
  var p1 = new Promise((n1, n2) => {
    n1(1000)
  })
  p1.then(() => {console.log(8)})
  console.log(9) 
  // 输出 : 1  9  8  3  4  7  6  2  5

在这里插入图片描述

Nodejs环境中的“事件循环”实现

异步任务中如何产生宏任务/微任务?

  • 宏任务
    • script全文。
    • setTimeout
    • setInterval
    • setImmediate,浏览器中没有
  • 微任务
    • promise 的 then 方法
    • async 方法中,await 语句行后面的代码
    • queueMicroTask(fn) 的 fn 方法 中
    • new MutationObserve(fn) 的方法中
    • process.nextTick,浏览器中没有

执行顺序:

  1. 执行默认宏任务(script全文代码) - 在此过程中添加其下级任务到队列中
  2. 循环执行 nextTick微任务队列
  3. 循环执行 其他微任务列表
  4. 再重复上面1,2,3 这3个步骤。不过不是执行默认宏任务,则是新添加的宏任务。
  5. 当所有其他宏任务执行完毕后,再循环执行 setImmediate 队列中的宏任务。

示例1(包括 setTimeout宏任务、nextTick微任务,普通微任务)

	console.log('1');
	async function async1() {
	    console.log('2');
	    await async2();
	    console.log('3');
	}
	async function async2() {
	    console.log('4');
	}
	
	process.nextTick(function() {
	    console.log('5');
	})
	
	setTimeout(function() {
	    console.log('6');
	    process.nextTick(function() {
	        console.log('7');
	    })
	    new Promise(function(resolve) {
	        console.log('8');
	        resolve();
	    }).then(function() {
	        console.log('9')
	    })
	})
	
	async1();
	
	new Promise(function(resolve) {
	    console.log('10');
	    resolve();
	}).then(function() {
	    console.log('11');
	});
	console.log('12');
	// 输出 : 1  2  4  10  12  5  3  11  6  8  7  9

在这里插入图片描述
示例2
包括:setTimeout宏任务,setImmediate宏任务,process.nextTick微任务,promise.then微任务

console.log('1');   // 同1

setTimeout(function () { // 宏1
    console.log('2');    // 宏1-同1
    process.nextTick(function () {  // 宏1-n微1
        console.log('3');
    })

    new Promise(function (resolve) {
        console.log('4');    // 宏1-同2
        resolve();
    }).then(function () {
        console.log('5')  // 宏1-微1
    })
})

new Promise(function (resolve) {
    console.log('6');   // 同2
    resolve();
}).then(function () {   // 微1
    console.log('7')
})

process.nextTick(function () {  // n微1
    console.log('8'); // 
})

setImmediate(() => {   // 宏2
    console.info('9')  // 主线程和事件队伍的函数执行完成之后立即执行  和setTimeOut(fn,0)差不多
})

new Promise(function (resolve) {
        console.log('10');   // 同3
        resolve();
    }).then(function () {  // 微2
    console.log('11')

})

setTimeout(function () {   // 宏3
    console.log('12');
    setImmediate(() => {
        console.info('13')
    })

    process.nextTick(function () {
        console.log('14')
    })

    new Promise(function (resolve) {
        console.log('15');
        resolve();
    }).then(function () {
        console.log('16')
    })
})

process.nextTick(function () {  // n微2
    console.log('17');
})

在这里插入图片描述

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

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

相关文章

数字孪生在灌区信息中的应用

灌区信息是智慧水利的组成部分,对灌区现代化改造的支撑作用和地位尤为重要,对促进水利可持续发展有重要意义。灌区信息化系统主要对对灌区的水情、雨情、土壤墒情、气象等信息进行监测,对重点区域进行视频监控,同时对泵站、闸门进…

C++ VTK三维图像体积计算Qt

程序示例精选 C VTK三维图像体积计算Qt 如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助! 前言 这篇博客针对《C VTK三维图像体积计算Qt》编写代码,代码整洁,规则,易读。 学习与…

基于SSM的乡镇篮球队管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

Windows,macOS,Linux换行标识的前世今生,如何处理文本文件行尾的^M

title: Windows,macOS,Linux换行标识的前世今生,如何处理文本文件行尾的^M / The Past and Present of Line Break Symbols in Windows, macOS, Linux: How to Deal with ^M at the End of Text Files categories: 极客实用技巧 / Geek Prac…

Java面试被问了几个简单的问题,却回答的不是很好

作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言! 前言 前几天参加了…

定时器之输出捕获

简介 • IC ( Input Capture )输入捕获 • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前 CNT 的值将被锁存到 CCR 中,可用于测量 PWM 波形的频率、占空比、脉冲间隔、电平持续时间等参数 • 每个高级定时器和…

防泄密软件推荐(数据防泄漏软件好用榜前五名)

在当今的数字化时代,数据已经成为企业最宝贵的资产之一。企业需要依赖数据来驱动业务决策、提高运营效率和创新产品。然而,随着数据量的不断增长,数据安全问题也日益凸显。企业需要采取有效的措施来保护敏感数据,防止信息泄露给竞…

5次多项式轨迹规划(博途SCL源代码)

运动控制轨迹规划时,加速度不连续将会使电机抖动,产生机械冲击。凸轮表轨迹规划很多都是基于5次多项式轨迹规划。3次多项式轨迹规划请查看下面文章链接: 3次多项式轨迹规划(PLC SCL代码)_RXXW_Dor的博客-CSDN博客机器人、运动控制等常用的轨迹规划有三次多项式、五次多项式…

第二章 进程与线程 十四、进程互斥的硬件实现方法(中断屏蔽法、TestAndSet指令、Swap指令)

目录 一、中断屏蔽法 3、优点: 4、缺点: 二、TestAndSet指令 3、代码解释 4、优点 5、缺点 三、Swap指令 1、定义 2、代码解释 3、优点 4、缺点 四、总结 一、中断屏蔽法 1、关中断后即不允许当前进程被中断,也必然不会发生进程切换。 2、…

【Tracking】Real-Time Camera Tracking: When is High Frame-Rate Best

paper: Real-Time Camera Tracking: When is High Frame-Rate Best? 0. 摘要: 更高的帧率承诺能更好地跟踪快速运动,但先进的实时视觉系统很少超过标准的10-60Hz范围,因为认为所需的计算量太大。实际上,在利用预测优势的跟踪器…

不添加端口号访问非80网站

自己用树莓派搭了个网站,由于某些原因,不能使用80端口,但又想访问网站时候不要添加端口号。这时候可以更换下DNS服务器,如使用cloudflare,替换成以下两个 参考文章 https://www.cnblogs.com/beidaxmf/p/16744589.html…

常见列表字典排序

一、列表排序 demoList [1, 3, 2, 4, 9 ,7]res sorted(demoList) # 默认升序# 降序 # res sorted(demoList, reverseTrue)print(res)二、字典排序 demoDict {"篮球": 5, "排球": 9, "网球": 6, "足球": 3}# sorted排序 res so…

Python_ithheima_第二阶段

第一章 01-初识对像 02 类的成员方法 03 类和对象 04 构造方法 05 魔术方法 06 封装 07 封装的课后练习题讲解 08 继承的基础语法 pass关键字的功能是“语法补全” 同名成员或方法,谁先来谁优先级高 09 复写父类成员和调用父类成员 10 变量的类型注解 11 函数和方法…

gitee-快速设置

快速设置— 如果你知道该怎么操作,直接使用下面的地址 HTTPS SSH: gitgitee.com:liuzl33078235/esp-idf.git 我们强烈建议所有的git仓库都有一个README, LICENSE, .gitignore文件 初始化 readme 文件 Git入门?查看 帮助 , Visual Studio / TortoiseG…

RabbitMQ 集群 - 普通集群、镜像集群、仲裁队列

目录 一、RabbitMQ 集群 1.1、前言 1.2、普通集群 1.3、镜像集群 1.4、仲裁队列 一、RabbitMQ 集群 1.1、前言 前面我们已经解决了消息可靠性问题,以及延迟消息问题 和 消息堆积问题. 这最后一章,我们就来解决以下 mq 的可用性 和 并发能力. 1.2、…

企业贸易站官网是HTML模板源码,提供完整源代码

企业贸易站官网是HTML模板源码,提供完整源代码二改的是 为了一起学习所以就分享出来了 有能力的可以自己做成主题配合帝国或者WordPress系统 (访问密码:2971)无需解压密码 源代码:https://url53.ctfile.com/f/20638…

Vue的自定义事件(Custom Events):实现组件间通信的强大工具

Vue的自定义事件(Custom Events):实现组件间通信的强大工具 Vue.js是一款流行的JavaScript框架,用于构建交互式的Web应用程序。在Vue中,组件是构建应用程序的基本单元,它们之间的通信对于构建复杂的应用非…

模拟实现offsrtof

写一个宏,计算结构体中某变量对于首地址的偏移 设计思路: 1.偏移量,用每个所求元素的地址减去结构体初始值的地址可得 2.如计算图示,第一个元素的偏移量结构体初始值必为0,第二个为4,其次为6,…

微信小程序更改AI类目-深度合成-AI绘画/AI问答教程

实测截图 准备材料: 1.营业执照 2.企业公章 一、首先我们需要到百度智能云进行企业认证 https://cloud.baidu.com/ 注册登录后进行企业认证 认证成功后申请千帆大模型平台和AI作画 开通付费服务 开通完以后点击右上角财务 选择合同管理 申请合同 将所有的选…

如何利用Arcgis进行地统计学分析(一):地统计学分析概念及其分析流程

一、地统计分析概念 地统计(Geostatistics)又称地质统计,也可以称为空间统计分析,其是统计学的一个分支。地统计学是以区域化变量理论为基础,以变异函数(variogram)为基本工具来研究分布于空间&…