JS的事件循环(Event Loop)

2024-03-08 1606阅读

温馨提示:这篇文章已超过385天没有更新,请注意相关的内容是否还可用!

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
                        

                        图解

                        JS的事件循环(Event Loop)

                        图解说明:

                        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 
                        

                        图解-执行顺序

                        JS的事件循环(Event Loop)

                        示例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
                        

                        JS的事件循环(Event Loop)

                        示例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
                        

                        JS的事件循环(Event Loop)

                        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
                              

                              JS的事件循环(Event Loop)

                              示例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');
                              })
                              

                              JS的事件循环(Event Loop)

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]