理解事件循环(从浏览器端到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循环嵌套
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Blockchain Basics
Daniel Drescher / Apress / 2017-3-16 / USD 20.99
In 25 concise steps, you will learn the basics of blockchain technology. No mathematical formulas, program code, or computer science jargon are used. No previous knowledge in computer science, mathema......一起来看看 《Blockchain Basics》 这本书的介绍吧!