最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 了解Event Loop

    正文概述 掘金(SwordQiu)   2020-12-27   267

    Event Loop事件循环简介

    JavaScript 是单线程的,由于单线程会造成I/O阻塞,比如发送请求时未响应就可能造成页面停滞,为了解决这个问题,浏览器开始支持异步JS,异步JS就是把一些异步任务(ajax、定时器)等放到任务队列中,然后通过事件循环不断读取、触发任务队列中的异步代码,这种机制就叫做事件循环Event Loop。

    Event Loop的核心代码是采用c++写的(属于NodeJs的范畴),本质上来说Event Loop就是采用轮询的方式不断读取、执行事件,今天我们要讨论的就是事件循环中的细节。

    阶段

    Event Loop内部分为以下阶段

       ┌───────────────────────┐
    ┌─>│        timers         │
    │  └──────────┬────────────┘
    │  ┌──────────┴────────────┐
    │  │     I/O callbacks     │
    │  └──────────┬────────────┘
    │  ┌──────────┴────────────┐
    │  │     idle, prepare     │
    │  └──────────┬────────────┘      ┌───────────────┐
    │  ┌──────────┴────────────┐      │   incoming:   │
    │  │         poll          │<─────┤  connections, │
    │  └──────────┬────────────┘      │   data, etc.  │
    │  ┌──────────┴────────────┐      └───────────────┘
    │  │        check          │
    │  └──────────┬────────────┘
    │  ┌──────────┴────────────┐
    └──┤    close callbacks    │
       └───────────────────────┘
    

    上面的每一个阶段都有一个队列(先进先出),里面存放回调函数。每当Event Loop到达一个阶段,一般来说都会执行队列中的某些函数(也有可能不操作)

    各阶段概览

    • timers 阶段:这个阶段执行 setTimeout 和 setInterval 的回调函数。
    • I/O callbacks 阶段:不在 timers 阶段、close callbacks 阶段和 check 阶段这三个阶段执行的回调,都由此阶段负责,这几乎包含了所有回调函数。
    • idle, prepare 阶段(看起来是两个阶段,不过这不重要):event loop 内部使用的阶段(我们不用关心这个阶段)
    • poll 阶段:获取新的 I/O 事件。在某些场景下 Node.js 会阻塞在这个阶段。
    • check 阶段:执行 setImmediate() 的回调函数。
    • close callbacks 阶段:执行关闭事件的回调函数,如 socket.on('close', fn) 里的 fn。

    一个 Node.js 程序结束时,Node.js 会检查 event loop 是否在等待异步 I/O 操作结束,是否在等待计时器触发,如果没有,就会关掉 event loop。

    timers

    这个阶段很有可能是Event Loop开始的第一个阶段,主要存放setTimeout或者setInterval等宏任务

    poll

    这个阶段主要用来获取新的I/O事件,当 event loop 进入 poll 阶段,发现 poll 队列为空,event loop 检查了一下最近的计时器,大概还有 100 毫秒时间,于是 event loop 决定这段时间就停在 poll 阶段,当定时器任务快开始的时候,Event Loop会绕过poll阶段进入check阶段

    check

    这个阶段有一个API,面试的时候经常用的到,属于nodeJS的setImmediate,它同样属于宏任务,但是相对于定时器来说,它的特点是要求更快执行

    setImmediate和setTimeout

    setImmediate 和 setTimeout 很相似,但是其回调函数的调用时机却不一样。

    从所属的阶段队列来看,setImmediate属于check阶段,setTimeout属于timers阶段,那么两者之间到底谁先执行呢?

    先看一段代码

    setTimeout(()=>{console.log('timeout')},0)
    setImmediate(()=>{console.log('immediate')},0)
    

    一般来说,都会优先执行setTmmediate,但是上面的代码实际执行顺序是这样的 了解Event Loop 为什么会有时先执行timeout,有时先执行immediate呢,这要从顺序看起,如果上面的代码setTimeout的时间设定为1000ms,那大家一定不会感到困惑,由于immediate存在于check阶段,当时间设定为1000ms时,Event Loop处于poll阶段,毕竟要等到1000ms才执行timers队列中的函数,所以Loop打算休息一下。

    然后呢好像时间差不多了,Loop发现check阶段有个immediate函数,于是跑过去执行一下,执行完了就再跑到timers阶段执行。

    而上面产生困惑的最大原因是定时器设置时间为0,这就要看Event Loop开始时,所处的阶段。

    如果Event Loop此时在timers阶段,队列中还没有定时器任务,又或者定时器任务还没到时间,那么必然会跳过此阶段,优先执行immediate任务。

    如果此时有任务,而且时间到了,那么必然会先执行setTimeout,这也是上述代码产生困惑的原因。

    下面我们对它进行改写

    setTimeout(() => {
      setTimeout(() => {
        console.log("timeout");
      }, 0);
    
      setImmediate(() => {
        console.log("immediate");
      });
    }, 1000);
    

    上面的代码,1秒后,执行箭头函数,此时Event Loop并不在timers阶段,由于顺序是不可变的,所以总是会优先执行immediate 了解Event Loop

    process.nextTick()

    你可能发现 process.nextTick() 这个重要的异步 API 没有出现在任何一个阶段里,那是因为从技术上来讲 process.nextTick() 并不是 event loop 的一部分。实际上,不管 event loop 当前处于哪个阶段,nextTick 队列都是在当前阶段后就被执行了。

    setTimeout(() => {
      setTimeout(() => {
        console.log("timeout");
      }, 0);
    
      setImmediate(() => {
        console.log("immediate");
      });
      
      process.nextTick(()=>{
         console.log('nexTick')
      })
    }, 1000);
    

    上面的代码执行顺序是这样的 了解Event Loop

    nextTick是在当前阶段马上执行,由于上面的代码执行后Loop处于poll阶段,所以会优先执行nextTick

    为了更好得实验,我们再改一下代码

    setTimeout(() => {
      setTimeout(() => {
        console.log("timeout");
        process.nextTick(() => {
          console.log("nexTick2");
        });
      }, 0);
    
      setImmediate(() => {
        console.log("immediate");
      });
    
      process.nextTick(() => {
        console.log("nexTick");
      });
    }, 1000);
    

    下面是结果,可以发现nextTick是在当前阶段马上执行的

    nexTick
    immediate
    timeout
    nexTick2
    

    process.nextTick() 和 setImmediate()

    这两个函数功能很像,而且名字也很令人疑惑。

    process.nextTick() 的回调会在当前 event loop 阶段「立即」执行。 setImmediate() 的回调会在后续的 event loop 周期(tick)执行。

    二者的名字应该互换才对。process.nextTick() 比 setImmediate() 更 immediate(立即)一些。

    这是一个历史遗留问题,而且为了保证向后兼容性,也不太可能得到改善。所以就算这两个名字听起来让人很疑惑,也不会在未来有任何变化。

    我们推荐开发者在任何情况下都使用 setImmediate(),因为它的兼容性更好,而且它更容易理解。

    宏任务和微任务

    异步任务中分宏任务和微任务,微任务总是比宏任务先执行

    常见宏任务

    了解Event Loop

    常见微任务

    了解Event Loop

    经典面试题

    setTimeout(()=> console.log(4))//宏任务
    
    new Promise(resolve => {
      resolve()//同步任务
      console.log(1) //同步任务
    }).then(()=> {
      console.log(3) //微任务
    })
    
    console.log(2) //同步任务
    

    改造成await

    setTimeout(_ => console.log(4)) //宏任务
    
    async function main() {
      console.log(1) //同步任务
      await Promise.resolve() //同步任务 相当于 resolve()
      console.log(3) //相当于promise.then //微任务
    }
    main()
    console.log(2) //同步任务
    

    起源地下载网 » 了解Event Loop

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元