簡體   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