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

    正文概述 掘金(度123)   2021-03-12   450

    同步任务和异步任务

       // test.js
       setTimeout(() => console.log(1));
       setImmediate(() => console.log(2));
       process.nextTick(() => console.log(3));
       Promise.resolve().then(() => console.log(4));
       (() => console.log(5))();
          
       // 5
       // 3
       // 4
       // 1
       // 2
    

    上边这段代码中 (() => console.log(5))(); 为同步任务,所以最先执行,剩下的为异步任务,

    其中

         process.nextTick(() => console.log(3));
         Promise.resolve().then(() => console.log(4));
    

    总是先于定时器执行,可以理解为 **process.nextTick和promise在下一轮事件循环之前执行,而执行到定时器的时候会计算时间阀阈值,达到时间阈值之后,添加入事件循环队列,**所以 总是3,4 早于 1,2执行

    总之 process.nextTick、promise是最先执行的异步任务

    其次,process.nextTick早于promise先执行,Node 执行完所有同步任务,接下来就会执行process.nextTick的任务队列,promise任务追加在process.nextTick队列之后

    所以以下代码总会先输出 3,再输出 4

          process.nextTick(() => console.log(3));
          Promise.resolve().then(() => console.log(4));
          // 3
          // 4
    

    需要注意的是,只有上一个队列清空完毕之后,才会执行下一个队列

        process.nextTick(() => console.log(1));
        Promise.resolve().then(() => console.log(2));
        process.nextTick(() => console.log(3));
        Promise.resolve().then(() => console.log(4));
        // 1
        // 3
        // 2
        // 4
    

    什么是event loop

    js是单线程的,事件循环使nodejs将非阻塞的I/O操作转移到系统内核来执行(windows平台下的 IOCP, *nix为自定义的线程池),它们可以在后台执行多个操作,当其中一个操作完成的时候,通知nodejs,以便将回调添加到轮询队列以最终执行

    当nodejs开始执行,处理输入的脚本,该脚本可能进行异步 api 调用、调度定时器或者调用 process.nextTock(),然后开始处理时间循环

    下图展示了事件循环操作顺序的简化概览:

    nodejs event-loop

    每个框都被称为事件循环机制的一个阶段,每个阶段都有一个先进先出的(FIFO)的回调队列,通常情况下,当事件循环进入给定的阶段,它将在该阶段执行队列中的回调,直到队列耗尽或者达到执行回调的最大数目,当队列耗尽或者达到执行回调的最大数目的时候,事件循环将进入到下一个阶段,依次类推

    阶段概述

    timers:在这个阶段执行setTimeout、setInterval的回调

    **pending callbacks:**执行延迟到下一个循环迭代的I/O回调

    **idle, prepare:**仅内部使用

    **poll:**这个阶段是轮询阶段,用于检索新的I/O事件,等待还未返回结果的 I/O 事件,执行与I/O相关的回调队列,如果没有其它的异步任务要处理(比如到期的定时器),则会一直停留在这个阶段,

    这里的回调,除了以下回调

    · setTimeout 和 setInterval的回调

    · setImmediate 的回调

    · 用于关闭请求的回调,如:socket.on('close',...)

    **check:**该阶段执行 setImmediate 的回调

    **close callbacks:**该阶段执行关闭回调,比如: socket.on('close',...)

    阶段的详细介绍

    timers

    计时器指定了一个阈值,在这个阈值之后可以执行一个提供的回调函数,注意阈值不是指执行的确切时间,计时器回调将在指定的时间过后尽快运行,执行其它操作被阻塞时会延迟执行

        const fs = require('fs');
        
        function someAsyncOperation(callback) {
          // 假设这个读取文件的操作需要 95ms 才能完成
          fs.readFile('/path/to/file', callback);
        }
        
        const timeoutScheduled = Date.now();
        
        setTimeout(() => {
          const delay = Date.now() - timeoutScheduled;
        
          console.log(`${delay}ms have passed since I was scheduled`);
        }, 100);
        
     
        someAsyncOperation(() => {
          const startCallback = Date.now();
        
          // 做一些其它操作 需要 10ms
          while (Date.now() - startCallback < 10) {
            // do nothing
          }
        });
    

    1. 当事件循环执行到轮询阶段时(在轮询阶段等待),此时它有一个空队列(fs.readFile尚未完成),

    2. 在等待的第 95ms 的时候,读取文件完成,并且将需要执行10ms的回调添加到轮询的回调队列,并执行,

    3. 回调完成之后,事件循环查看到已经达到阈值的计时器,然后返回到计时器阶段执行计时器回调

    4. 在此示例中将看到计划计时器,与执行计时器回调总为 105ms

    pending callbacks

    这个阶段会执行一些系统操作的回调,如 TCP 错误的类型,例如,如果 TCP 的 socket在尝试连接时收到的 'ECONNREFUSED',一些 *nix 系统希望等待报告错误,这将会在 pending callbacks 阶段的回调队列执行

    poll

    这个阶段主要做两件事情

    1. 计算它应该阻塞多长时间,并轮询 I/O

    2. 处理轮询队列中的回调

    当事件循环进入 poll 阶段,并没有计时器任务时,将发生以下这两种情况之一:

    1. 如果轮询回调队列不是空的,则事件循环将依次同步执行它们,

    2. 如果轮询回调队列是空的,则将发生另外两种情况之一:

    1. 如果安排了 setImmediate,则事件循环将结束轮询阶段,并继续进入check阶段,执行回调队列

    2. 如果没有安排 setImmediate,则事件循环将继续等待回调脚本添加到当前阶段的队列

    此外,只要轮询队列为空,事件循环将检查已经到达阈值的计时器,如果一个或者多个计时器准备好了,将回滚到计时器阶段来执行这些计时器回调队列

    check

    通常代码执行时,事件循环最终将达到轮询阶段,在轮询阶段它将等待传入的连接、请求等I/O事件,但是如果已经使用setImmediate安排了回调,并且轮训阶段回调队列为空,则它将进入检查阶段,而不是继续等待轮训事件,

    close callbacks

    如果一个 socket 或者 handle 突然关闭(例如:socket.destory()),则将在此阶段触发close事件

    setImmediate和setTimeout

    由于setTimeout 在timers 阶段执行,setImmediate 是在 check 阶段执行的,理论上来讲当 setTimeout 第二个参数即阈值设置为 0 的时候,setTimeout 会比 setImmdiate 先执行,即

          setTimeout(() => console.log(1), 0);
          setImmediate(() => console.log(2));
    

    上述代码执行顺序为 1 , 2,但是 实际情况是不确定的,因为nodejs 是做不到 0ms的,当第二个参数大于2147483647或小于1时,将自动设置为1,非整数的将自动截取为整数

    实际执行时,进入事件循环的时候如果到了 1ms,则会先执行 setTimeout,如果没有到 1ms 则会跳过 timers 阶段,进入 check 阶段,先执行 setImmediate,所以这受到当前进程的性能影响,

    但是如果在 I/0 回调内调用这两个任务,则setImmediate 一定先于 setTimeout 执行

        const fs = require('fs');
        
        fs.readFile('test.js', () => {
          setTimeout(() => console.log(1));
          setImmediate(() => console.log(2));
        });
    

    上述代码会先进入 poll 阶段,然后进入 check 阶段,最后再进入 timers阶段

    参考链接:

    nodejs.org/en/docs/gui…

    www.ruanyifeng.com/blog/2018/0…


    起源地下载网 » nodejs event-loop

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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