理解事件循环(从浏览器端到node端)

栏目: JavaScript · 发布时间: 5年前

内容简介:js区别于其他语言即在于它的单线程特性,考虑到它的主要执行环境(浏览器),这样设计也是情理之中:事件驱动的js程序难免在程序设计时出现诸多事件,而依靠单线程来执行这些事件,若只是按部就班当一个事件执行完成后再执行另一个事件,那当遇到费时很长的比如ajax请求时,程序岂不是要死掉,依据互联网行业的八秒原则,怕是用户要跑的差不多了。遇到这种情况,如果是别的语言,还可以利用多线程将事件分发,避免造成阻塞。而js的解决方案则是异步处理事件,提到异步则是依赖于我们要谈的事件循环(Event Loop)。下面我们会从浏

1.单线程的js

js区别于其他语言即在于它的单线程特性,考虑到它的主要执行环境(浏览器),这样设计也是情理之中:

Web Worker

2.为什么要有事件循环

事件驱动的js程序难免在程序设计时出现诸多事件,而依靠单线程来执行这些事件,若只是按部就班当一个事件执行完成后再执行另一个事件,那当遇到费时很长的比如ajax请求时,程序岂不是要死掉,依据互联网行业的八秒原则,怕是用户要跑的差不多了。遇到这种情况,如果是别的语言,还可以利用多线程将事件分发,避免造成阻塞。而js的解决方案则是异步处理事件,提到异步则是依赖于我们要谈的事件循环(Event Loop)。下面我们会从浏览器和node环境来谈各自的事件循环具体实现。

3.浏览器端

先从一张图看一下浏览器事件循环的运行机制(图片稍有不完整,会在后面解释中补充) 理解事件循环(从浏览器端到node端)

  • 执行栈:
    • 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> 标签,作为程序入口,所以我们前面说是对异步事件的划分有点不严谨), setTimeoutsetInterval
      • 微事件: promise
    • 这里主要是为了解决 event loop 在拉取事件时像下面这种情况的尴尬
      //event-table注册了以下事件
      setTimeout(()=>{},0);
      new Promise(function (resolve,reject){
          resolve()
      }).then(()=>{
          //statement
      })
      //到底谁应该被先入队复制代码
    • 所以在事件循环中将事件队列分为宏事件队列和微事件队列,异步事件将在有结果时会被分发进各自的队列。
    • 对于宏事件和微事件的执行原则
script
  • 一张图去理解(引用地址: https://juejin.im/post/59e85eebf265da430d571f89
  • 理解事件循环(从浏览器端到node端)

    • 到这我们的浏览器端的事件循环就结束了,接下来是node环境中的事件循环,先来张图过渡一下。(图片来自李锴的《新时期的node.js入门》)

      理解事件循环(从浏览器端到node端)

    4.node端

    • 作为服务端的js运行环境,node在处理高并发的服务端需求时表现极为出色,而这一特性也于我们的事件循环息息相关。
    • 从前面的图来看,node的事件循环是按阶段来的,下面我们来分阶段介绍
      • timers:这里维护着一个专门针对 setTimeout , setInterval 的事件队列。定时器会在有结果(到达延时时间时)被注册进这个队列中后面称 timer queue
      • IO callbacks:这个阶段处理一些上一轮循环少数未执行的I/O回调。
      • idle,prepare:内部实现,与代码中事件循环无关。
      • poll:这个阶段可以看成整个事件循环的主导者,绝大部分事件循环在这个部分完成。具体过程后面专门介绍。
      • check:本阶段主要维护针对 setImmediate(在当前事件循环的结尾执行) 的事件队列(后面称 check queue )。
      • close callback:主要处理一些关闭连接的回调。 理解事件循环(从浏览器端到node端)
    • 主要来说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
    • 理解事件循环(从浏览器端到node端)

    5.后记

    根据个人理解以及整理得,有错误或者偏差敬请原谅,欢迎指正。


    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

    查看所有标签

    猜你喜欢:

    本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

    Linux程序设计

    Linux程序设计

    马修 / 陈健 / 人民邮电出版社 / 2007-7 / 89.00元

    《Linux 程序设计(第3版)》讲述在Linux系统及其他UNIX风格的操作系统上进行的程序开发,主要内容包括标准Linux C语言函数库和由不同的Linux或UNIX标准指定的各种工具的使用方法,大多数标准Linux开发工具的使用方法,通过DBM和MySQL数据库系统对Linux中的数据进行存储,为X视窗系统建立图形化用户界面等。《Linux 程序设计(第3版)》通过先介绍程序设计理论,再以适......一起来看看 《Linux程序设计》 这本书的介绍吧!

    正则表达式在线测试
    正则表达式在线测试

    正则表达式在线测试

    RGB CMYK 转换工具
    RGB CMYK 转换工具

    RGB CMYK 互转工具

    HEX HSV 转换工具
    HEX HSV 转换工具

    HEX HSV 互换工具