理解事件循环(从浏览器端到node端)
栏目: JavaScript · 发布时间: 5年前
内容简介:js区别于其他语言即在于它的单线程特性,考虑到它的主要执行环境(浏览器),这样设计也是情理之中:事件驱动的js程序难免在程序设计时出现诸多事件,而依靠单线程来执行这些事件,若只是按部就班当一个事件执行完成后再执行另一个事件,那当遇到费时很长的比如ajax请求时,程序岂不是要死掉,依据互联网行业的八秒原则,怕是用户要跑的差不多了。遇到这种情况,如果是别的语言,还可以利用多线程将事件分发,避免造成阻塞。而js的解决方案则是异步处理事件,提到异步则是依赖于我们要谈的事件循环(Event Loop)。下面我们会从浏
1.单线程的js
js区别于其他语言即在于它的单线程特性,考虑到它的主要执行环境(浏览器),这样设计也是情理之中:
Web Worker
2.为什么要有事件循环
事件驱动的js程序难免在程序设计时出现诸多事件,而依靠单线程来执行这些事件,若只是按部就班当一个事件执行完成后再执行另一个事件,那当遇到费时很长的比如ajax请求时,程序岂不是要死掉,依据互联网行业的八秒原则,怕是用户要跑的差不多了。遇到这种情况,如果是别的语言,还可以利用多线程将事件分发,避免造成阻塞。而js的解决方案则是异步处理事件,提到异步则是依赖于我们要谈的事件循环(Event Loop)。下面我们会从浏览器和node环境来谈各自的事件循环具体实现。
3.浏览器端
先从一张图看一下浏览器事件循环的运行机制(图片稍有不完整,会在后面解释中补充)
- 执行栈:
- js引擎在执行js代码时,会维护一个执行栈,这个栈用来存放要执行的事件。
- 执行栈中只会存在一个事件,所谓js的执行过程不过就是各种事件不断入栈->执行->出栈的过程罢了。
- 同步/异步事件
- 记得前面有提到js对于事件的异步处理方法,在这里事件被分为同步事件和异步事件。
- 同步事件比如一些立马执行的语句,比如很常见的
console.log("我是同步的")
,而异步事件则比如一些ajax
请求的回调函数,一些鼠标,键盘事件等。 - 执行过程
- js在执行时(从上到下,逐条执行)碰到同步事件则会压入执行栈,然后被执行,出栈。而当遇到异步事件则会被注册进
Event-Table
中,待事件有结果(比如ajax请求完成,setTimeout
到了延时的时间)时就会被推入 事件队列 中,就是上图中的callback queue
。当同步任务执行完成后(即主线程中执行栈为空时),event loop
就会去检查我们这里的是事件队列,若存在事件则依次进入执行栈被执行。 - 说了这么多,举个栗子吧
setTimeout(()=>{ console.log('我是延时为3s的定时器') },3000) setTimeout(()=>{ console.log('我是延时为0的定时器') },0) console.log('我是同步的')复制代码
- 先来看执行效果
我是同步的 我是延时为0的定时器 我是延时为3s的定时器复制代码
- 执行过程:
- 先碰到
setTimeout(3s)
,异步事件,将他注册进event-table
中。 - 接下来是
setTimeout(0s)
,同样是异步事件,注册进event-table
中。 - 接着碰到
console.log('我是同步的')
识别为同步事件,进入执行栈,执行,之后出栈。 - 此时我们的执行栈已经为空,
event loop
就会去事件队列中去找是不是有待执行的异步事件。此时延时为0s的定时器肯定已经在队列中了,进入执行栈执行对应的回调函数。接着3s时延时为3s的定时器也会进入事件队列中,和上面的一样被执行。(这里我们看到延时的时间只是它被注册进事件队列的时间,而实际被执行的时间或许会因为事件过多而有延迟,毕竟执行栈中始终只会有一个事件被执行, 资源有限,先到先得 ) - 其实所谓事件循环可以理解成一个死循环,始终在执行栈于事件队列之间交替检查 。
- 在上面,我们将事件分为同步事件和异步事件,而实际中对异步事件(也不能完全说是对异步事件的划分,后面解释)有更细的划分。
- microTask(微事件)和macroTask(宏事件)
- 宏事件:
script
(这里经常指的是<script>
标签,作为程序入口,所以我们前面说是对异步事件的划分有点不严谨),setTimeout
,setInterval
等 - 微事件:
promise
等 - 这里主要是为了解决
event loop
在拉取事件时像下面这种情况的尴尬//event-table注册了以下事件 setTimeout(()=>{},0); new Promise(function (resolve,reject){ resolve() }).then(()=>{ //statement }) //到底谁应该被先入队复制代码
- 所以在事件循环中将事件队列分为宏事件队列和微事件队列,异步事件将在有结果时会被分发进各自的队列。
- 对于宏事件和微事件的执行原则
script
- 到这我们的浏览器端的事件循环就结束了,接下来是node环境中的事件循环,先来张图过渡一下。(图片来自李锴的《新时期的node.js入门》)
4.node端
- 作为服务端的js运行环境,node在处理高并发的服务端需求时表现极为出色,而这一特性也于我们的事件循环息息相关。
- 从前面的图来看,node的事件循环是按阶段来的,下面我们来分阶段介绍
- timers:这里维护着一个专门针对
setTimeout
,setInterval
的事件队列。定时器会在有结果(到达延时时间时)被注册进这个队列中后面称timer queue
。 - IO callbacks:这个阶段处理一些上一轮循环少数未执行的I/O回调。
- idle,prepare:内部实现,与代码中事件循环无关。
- poll:这个阶段可以看成整个事件循环的主导者,绝大部分事件循环在这个部分完成。具体过程后面专门介绍。
- check:本阶段主要维护针对
setImmediate(在当前事件循环的结尾执行)
的事件队列(后面称check queue
)。 - close callback:主要处理一些关闭连接的回调。
- 主要来说poll阶段
- poll:这里我们把他解释为 轮询
- poll主要干两件事:
- 检查timer维护的事件的事件队列中是否有事件被分发进队列
- 执行poll阶段自己维护的事件队列
- 进入poll阶段时:
poll queue poll queue timer queue
process.nextTick
- 用来定义一个异步事件,这个事件将在当前事件循环阶段结束后执行,注意与
setImmediate
的区别,所以说如果这两个同时存在于一轮循环中,process.nextTick总是在setImmediate
之前执行 - 这个方法定义的事件将被分发到
nextTick queue
中。
- node中也将异步事件分为宏事件和微事件来管理执行顺序,执行原则在最新的node标准中于浏览器一致,这里有一张图很清晰的解释执行顺序(图片来源: https://juejin.im/post/5c337ae06fb9a049bc4cd218 )
5.后记
根据个人理解以及整理得,有错误或者偏差敬请原谅,欢迎指正。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 浏览器和Node中的事件循环机制
- 浏览器事件循环机制与Vue nextTick的实现
- JS异步详解 - 浏览器/Node/事件循环/消息队列/宏任务/微任务
- 008.Python循环for循环
- 006.Python循环语句while循环
- 007.Python循环语句while循环嵌套
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Linux程序设计
马修 / 陈健 / 人民邮电出版社 / 2007-7 / 89.00元
《Linux 程序设计(第3版)》讲述在Linux系统及其他UNIX风格的操作系统上进行的程序开发,主要内容包括标准Linux C语言函数库和由不同的Linux或UNIX标准指定的各种工具的使用方法,大多数标准Linux开发工具的使用方法,通过DBM和MySQL数据库系统对Linux中的数据进行存储,为X视窗系统建立图形化用户界面等。《Linux 程序设计(第3版)》通过先介绍程序设计理论,再以适......一起来看看 《Linux程序设计》 这本书的介绍吧!