Javascript 为什么在Nodejs事件循环';什么有用?

Javascript 为什么在Nodejs事件循环';什么有用?,javascript,node.js,event-loop,Javascript,Node.js,Event Loop,我读了很多相关文件,但我还是不明白它是如何工作的 const fs = require('fs') const now = Date.now(); setTimeout(() => console.log('timer'), 10); fs.readFile(__filename, () => console.log('readfile')); setImmediate(() => console.log('immediate')); while(Date.now() - n

我读了很多相关文件,但我还是不明白它是如何工作的

const fs = require('fs')
const now = Date.now();

setTimeout(() => console.log('timer'), 10);
fs.readFile(__filename, () => console.log('readfile'));
setImmediate(() => console.log('immediate'));
while(Date.now() - now < 1000) {
}
以及第二段代码日志

timer
immediate
我想没关系

问题: 我不明白为什么第一段代码会记录

immediate
readfile
我认为该文件已被完全读取,其回调函数在1秒后将I/O回调阶段的queque排入队列

然后我认为事件循环将移动到
计时器(无)
I/O回调(fs.readFile的回调)
空闲/准备(无)
轮询(无)
检查(setImmediate的回调)
,最后按顺序关闭回调(无),但结果是
setImmediate()
仍然首先运行。

设置超时(()=>console.log('timer'),10);
setTimeout(() => console.log('timer'), 10);                
fs.readFile(__filename, () => console.log('readfile'));    
setImmediate(() => console.log('immediate'));               

while(Date.now() - now < 1000) {
}
fs.readFile(u文件名,()=>console.log('readFile'); setImmediate(()=>console.log('immediate'); while(Date.now()-now<1000){ }
解释

  • setTimeout
    计划在10毫秒后放入事件循环

  • 异步文件读取开始

  • 非标准的
    setImmediate
    计划显示中断长进程的控制台输出

  • 一秒钟的阻塞循环运行。控制台中还没有任何内容

  • setImmediate
    在循环过程中打印
    immediate
    控制台消息

  • 文件读取结束,即使在执行
    循环后也会执行回调
    结束控制台输出
    readfile
    现在已存在

  • 最后,控制台消息
    timer
    在大约10秒后打印出来

  • 需要注意的事项

    • 上述命令(循环除外)均不同步。他们 安排某事并立即执行下一个命令

    • 回调函数仅在当前阻塞之后调用 行刑结束了

    • 超时命令不保证在指定的时间执行 间隔保证它们将在之后的任何时间运行 间隔时间

    • setImmediate
      非常具有实验性


    您看到的行为是因为事件循环中有多种类型的队列,并且系统按照事件类型的顺序运行事件。它不仅仅是一个巨大的事件队列,其中所有内容都按照FIFO顺序运行,具体取决于它添加到事件队列的时间。相反,它喜欢运行一种类型的所有事件(达到一个限制),前进到下一种类型,运行所有这些事件,等等

    而且,I/O事件只在循环中的一个特定点添加到它们的队列中,因此它们被强制进入特定的顺序。这就是为什么
    setImmediate()
    回调会在
    readFile()
    回调之前执行,即使当
    while
    循环完成时,两者都准备就绪

    然后我认为事件循环将依次移动到计时器(none)、I/O回调(fs.readFile的回调)、空闲/准备(none)、轮询(none)、检查(setImmediate的回调)和最后关闭回调(none),但结果是setImmediate()仍然首先运行

    setTimeout(() => console.log('timer'), 10);                
    fs.readFile(__filename, () => console.log('readfile'));    
    setImmediate(() => console.log('immediate'));               
    
    while(Date.now() - now < 1000) {
    }
    
    问题是,事件循环的I/O回调阶段运行已经在事件队列中的I/O回调,但完成后不会自动放入事件队列。相反,它们只会在
    I/O poll
    步骤中的稍后过程中放入事件队列(请参见下图)。因此,第一次通过I/O回调阶段时,还没有要运行的I/O回调,因此您不会在您认为需要时获得
    readfile
    输出

    但是,
    setImmediate()
    回调在第一次通过事件循环时就准备好了,因此它在
    readFile()
    回调之前开始运行

    I/O回调的延迟添加很可能解释了为什么
    readFile()
    回调发生在最后而不是
    setImmediate()
    回调之前

    以下是
    while
    循环完成时发生的情况:

  • 当while循环完成时,它将以计时器回调开始,并看到计时器已准备好运行,因此它将运行该计时器
  • 然后,它将运行任何已经存在但还没有的I/O回调。尚未收集来自
    readFile()
    的I/O回调。它将在本周期晚些时候收集
  • 然后,它将经历其他几个阶段,并进入I/O轮询。有一个函数收集
    readFile()
    回调事件并将其放入I/O队列(但尚未运行)
  • 然后,它进入checkHandlers阶段,在那里运行
    setImmediate()
    回调
  • 然后,它再次启动事件循环。没有计时器,因此它转到I/O回调,最后找到并运行
    readFile()
    回调

  • 因此,对于那些不太熟悉事件循环过程的人,让我们更详细地记录代码中实际发生的事情。运行此代码时(将计时添加到输出中):

    我添加了以秒为单位的计时,相对于程序启动的时间,这样您就可以看到执行的时间

    下面是发生的情况:

  • 计时器启动并设置为10毫秒,其他代码继续运行
  • fs.readFile()
    操作启动后,其他代码继续运行
  • setImmediate()
    已注册到事件系统,并且其事件位于相应的事件队列中,其他代码将继续运行
  • 循环开始循环时
  • while
    循环期间,
    fs.readFile()
    完成其工作(在后台运行)。它的事件已准备就绪,但尚未在相应的事件队列中(稍后将对此进行详细介绍)
  • 循环完成
    setTimeout(() => console.log('timer'), 10);                
    fs.readFile(__filename, () => console.log('readfile'));    
    setImmediate(() => console.log('immediate'));               
    
    while(Date.now() - now < 1000) {
    }
    
    const fs = require('fs')
    
    let begin = 0;
    function log(msg) {
        if (!begin) {
            begin = Date.now();
        }
        let t = ((Date.now() - begin) / 1000).toFixed(3);
        console.log("" + t + ": " + msg);
    }
    
    log('start program');
    
    setTimeout(() => log('timer'), 10);
    setImmediate(() => log('immediate'));
    fs.readFile(__filename, () => log('readfile'));
    
    const now = Date.now();
    log('start loop');
    while(Date.now() - now < 1000) {}
    log('done loop');
    
    0.000: start program
    0.004: start loop
    1.004: done loop
    1.005: timer
    1.006: immediate
    1.008: readfile
    
    const fs = require('fs')
    
    let begin = 0;
    function log(msg) {
        if (!begin) {
            begin = Date.now();
        }
        let t = ((Date.now() - begin) / 1000).toFixed(3);
        console.log("" + t + ": " + msg);
    }
    
    log('start program');
    
    setImmediate(() => log('immediate'));
    setTimeout(() => log('timer'), 10);
    
    const now = Date.now();
    log('start loop');
    while(Date.now() - now < 1000) {}
    log('done loop');
    
    0.000: start program
    0.003: start loop
    1.003: done loop
    1.005: timer
    1.008: immediate
    
    // timeout_vs_immediate.js
    const fs = require('fs');
    
    fs.readFile(__filename, () => {
      setTimeout(() => {
        console.log('timeout');
      }, 0);
      setImmediate(() => {
        console.log('immediate');
      });
    });
    
     immediate
     timeout