繁体   English   中英

Node.JS中的事件循环如何可预测

[英]How predictable is the event loop in Node.JS

如果您观察到像这样的简单客户端JS,很直观,如果遇到setTimeouts,则时间长度(在本示例中为0 ms)确定JS运行时实际上将其添加到队列后的时间间隔,如下所述在这里

但是,在Node.JS中,如果我有一些基本上是异步的api调用(如fs.readFile()),并且如果我有一个简单的setTimeout,则在同一代码库中,我的理解是事件循环将开始读取文件,如果已经读取,它将继续进行排队并将其排队,以便一旦主节点线程未执行任何顺序操作就可以触发适当的回调。 我的问题是,仅在特定超时仍然有效之后(与类客户端JS相反),“添加setTimeout回调”这一概念是否存在? 具体来说,这是一个示例:

const fs = require('fs');
// Set timeout for 2 secs
setTimeout(function() { 
  console.log('Timeout ran at ' + new Date().toTimeString()); 
}, 2000);
// Read some file
fs.readFile('sampleFile.txt', function(err, data) {
   if(err) {
     throw err;
   }
   console.log(data.toString() + " " + new Date().toTimeString();
}
var start = new Date();
console.log('Enter loop at: '+start.toTimeString());
// run a loop for 4 seconds
var i = 0;
// increment i while (current time < start time + 4000 ms)
while(new Date().getTime() < start.getTime() + 4000) {
  i++;
}
console.log('Exit loop at: ' +new Date().toTimeString() +'. Ran '+i+' iterations.');

我为此得到的输出是:

在以下时间进入循环:GMT-0700(PDT)18:22:14(退出)在以下时间退出循环:GMT-0700(PDT)18:22:18。 跑了33980131次迭代。 超时在格林尼治标准时间07:00(PDT)18:22:18运行SampleFileContents格林尼治标准时间07:00(PDT)18:22:18

这是否意味着在完全读取文件之前,将setTimeout回调+消息以某种方式放置在事件循环队列中? 这告诉我3件事情中的1件:将setTimeout回调+消息放在队列中,准备在2秒和下一个可用滴答之后触发。 或实际读取sampleFile.txt的时间超过2秒。 或快速读取了sampleFile.txt,但由于某种原因,它没有放在事件循环队列中的setTimeout之前。

我是否使用正确的思维模式来考虑这个问题? 我试图更深入地了解节点的内部结构,而不必深入研究libuv / libeio C代码。 我尝试过处理超时,有趣的是,当我将超时设置为大于4000毫秒时,似乎在我的输出中,我总是打印出sampleFileContents,然后才实际打印超时时间。

有一个陷阱。 以下代码行是同步的和阻塞的。

// increment i while (current time < start time + 4000 ms)
while(new Date().getTime() < start.getTime() + 4000) {
  i++;
}

这意味着事件循环被它劫持了,并且永远无法像您期望的那样起作用。

在文件settmeout之前进行settmeout打印可能意味着在循环开始之前的时间点已经设置了计时器,但是尚未添加文件读取事件。 我添加了更多代码来验证这个想法。

var readStream = fs.createReadStream('sampleFile.txt');

  readStream.on('open', function () {
    console.log('Read started ' + new Date().toTimeString());
  });

  readStream.on('data', function(data) {
  });

  readStream.on('end', function(err) {
   console.log('Read end ' + new Date().toTimeString());
  });   

setTimeout(function() {
  console.log('Timeout ran at ' + new Date().toTimeString());
}, 2000);

var start = new Date();
console.log('Enter loop at: '+start.toTimeString());

var i = 0;
while(new Date().getTime() < start.getTime() + 4000) {
  i++;
}

console.log('Exit loop at: ' +new Date().toTimeString() +'. Ran '+i+' times.');

输出为:

Enter loop at: 22:54:01 GMT+0530 (IST)
Exit loop at: 22:54:05 GMT+0530 (IST). Ran 34893551 times.
Timeout ran at 22:54:05 GMT+0530 (IST)
Read started 22:54:05 GMT+0530 (IST)
Read end 22:54:05 GMT+0530 (IST)

这证明了我的理论,即它们从未同时运行。 至于发生这种情况的原因,我相信fs事件至少需要一个滴答声才能排队并正确发送。 但是超时会立即添加。 由于您在添加文件读取事件之前锁定了事件循环,因此该事件在循环结束后在超时处理程序之后排队。

您可以尝试运行没有循环的代码,输出将是

Enter loop at: 22:57:15 GMT+0530 (IST)
Exit loop at: 22:57:15 GMT+0530 (IST). Ran 0 iterations.
 22:57:15 GMT+0530 (IST)
Timeout ran at 22:57:17 GMT+0530 (IST)

如果读取先完成。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM