• Node.js事件循环


    什么是事件循环

    事件循环允许Node.js执行一些异步非阻塞I/O操作的机制或者策略。

    尽管JavaScript是单线程的,所以这些操作都尽量交给操作系统的内核去做。 大多数现代操作系统内核都是多线程的,所以它们可以处理在后台执行的多个操作。当其中一个操作完成时,内核会告诉Node.js,将适当的回调添加到轮询队列中,以便最终执行。

    事件循环的6个阶段

    当Node.js启动时,会初始化事件循环,然后处理在Node.js里运行的代码,包括各种API的调用,定时器,process.nextTick(),然后运行事件循环机制。

    事件循环

    6阶段都分别做了什么?

    • timers: setTimeout(),setInterval()在这个阶段被执行
    • pending callbacks: 执行一些系统错误的处理,比如TCP,UDP,Socket错误等等。
    • idle, prepare: only used internally.
    • poll: 轮询阶段,向系统获取新的I/O事件,执行I/O回调。首先会先处理到期的定时器的回调,然后处理poll队列里的回调,直到队列里的回调全部被清空,或者达到一个处理的最大值,如果poll队列不为空且刚好有setImmediate,则停止当前的poll阶段,前往check阶段,执行setImmediate回调。如果没有setImmediate,Node.js会去查看有没有定时器任务到期了,如果有的话,就前往timers阶段,执到期行定时器的回调。
      retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate.
    • check: 执行setImmediate() 回调,且setImmediate只能在check阶段执行。
    • close callbacks: 执行结束的回调。

    每个阶段都有一个FIFO回调队列要执行。虽然每个阶段都有其特殊的方式,但通常情况下,当事件循环进入给定阶段时,它将执行特定于该阶段的任何操作,然后在该阶段的队列中执行回调,直到队列耗尽或执行了最大数量的回调。当队列耗尽或达到回调限制时,事件循环将移动到下一个阶段,依此类推。

    process.nextTick()

    图中没有显示process.nextTick(),尽管它是异步API的一部分。这是因为process.nextTick()在技术上不是事件循环的一部分。 是在任意两个阶段中间,只要有process.nextTick()没有被执行,那么优先执行process.nextTick的回调。

    测试代码

    const { readFile } = require('fs');
    const EventEmitter = require('events');
    
    class EE extends EventEmitter{}
    const yy = new EE();
    yy.on('event',() => {
        console.log('出大事了!');  //2
    })
    
    setTimeout(() => {  //5
        console.log('0 毫秒到期执行的定时器回调')
    },0)
    
    setTimeout(() => { //
        console.log('100 毫秒到期执行的定时器回调')
    },100)
    
    setTimeout(() => {  //
        console.log('200 毫秒到期执行的定时器回调')
    },200)
    
    readFile('./package.json','utf-8',data => {
        console.log('完成文件1读操作的回调');  //7
    })
    
    readFile('./task.js','utf-8',data => {
        console.log('完成文件2读操作的回调');  //8
    })
    
    setImmediate(() => {
      console.log('immediate 立即回调'); //6
    })
    
    process.nextTick(() => { //1
        console.log('process 的第一次回调');
    })
    
    Promise.resolve()
    .then(() => {
      yy.emit('event');  
      process.nextTick(() => {
        console.log('process 的第二次回调'); //5
      }) 
      console.log('Promise的第一次回调'); //3
    }).then(() => {
        console.log('Promise的第2次回调'); //4
    })
    

    执行结果是什么?
    注:文件1,文件2的大小会影响操作结果。

    process 的第一次回调
    出大事了!
    Promise的第一次回调
    Promise的第2次回调
    process 的第二次回调
    0 毫秒到期执行的定时器回调
    immediate 立即回调
    完成文件2读操作的回调
    完成文件1读操作的回调
    100 毫秒到期执行的定时器回调
    200 毫秒到期执行的定时器回调
    

    你的结果是正确的嘛?想想为什么。

    参考链接:The Node.js Event Loop, Timers, and process.nextTick()

  • 相关阅读:
    docker 安装es
    Redis 和 Zookeeper 到底谁更牛?
    Redisson 看门狗
    记一次线上服务CPU 100%的处理过程
    必须了解的mysql三大日志-binlog、redo log和undo log
    python学习笔记 -- reduce合并减少
    Python学习笔记 -- 列表2: 遍历:嵌套列表, 将其中同位置的元素组成新的列表
    python学习笔记 -- filter() 过滤符合条件的可迭代序列
    python学习笔记 -- map() 操作可迭代序列
    python学习笔记
  • 原文地址:https://www.cnblogs.com/JessicaIsEvolving/p/10373451.html
Copyright © 2020-2023  润新知