nodejs-第二章-第二節-nodejs事件循環(2-1)

當Node.js啓動時會初始化event loop,每個event loop 都會包含按如下六個節點循環,nodejs事件循環和瀏覽器事件循環完全不一樣。

nodejs事件循環

  • 圖中的每個方框被稱作事件循環的一個"階段",這6個階段爲一輪事件循環;
階段概覽
  • timers(定時器): 此階段執行那些有setTimeout() 和 setInterval()調度的回調函數;
  • I/O callbacks(I/O回調):此階段會執行幾乎所有的回調函數,除了close callbacks(關閉回調) 和 那些有timers與setImmediate()調度的回調;
  • idle(空轉),prepare: 此階段只在內部使用;
  • poll(輪詢): 檢索新的I/O事件;在恰當的時候Node會阻塞在這個節點;
  • check(檢查): setImmediate() 設置的回調會在此階段被調用;
  • close callbacks(關閉事件的回調):
    • 如:socket.on('close',...);此類的回調會在此階段被調用; 在事件循環每次運行之間,Node.js會檢查它是否等待異步I/O或定時器,如果沒有的話就會自動關閉;

如果event loop 進入了poll階段,且代碼未設定timer,將會發生下面的情況:

  • 如果poll queue不爲空,event loop將同步的執行queue裏的callback,知道queue爲空,或執行的callback達到系統的上限;
  • 如果poll queue爲空,將會發生下面的情況:
    • 如果代碼已經被setImmediate()設定了callback,event loop 將結束poll階段進入check階段,並執行check階段的queue(check階段的queue是setImmediate設定的)
    • 如果代碼沒有設定setImmediate(callback),event loop將阻塞在該階段等待callbacks加入poll queue,一旦達到就立即執行;

如果event loop進入了poll階段,且代碼設定了timer:

  • 如果poll queue進入空狀態時(即poll階段爲空閒狀態),event loop將阻塞在該階段等待callbacks加入poll queue,一旦達到就立即執行;

setImmediate約定於setTimeout(cb,0)
  • path.resolve() 方法會把一個路徑或路徑片段的序列解析爲一個絕對路徑。

  • __dirname 總是指向當前文件夾的絕對路徑

  • __filename 總是指向當前文件的絕對路徑

  • note:

    • io: 瀏覽器線程去調用一些異步的回調
  • 執行代碼1
var fs = require('fs');
var path = require('path');
function someAsyncOperation(callback) {
  // 花費2ms
  fs.readFileSync('./read.txt', callback);
}
var timeoutScheduled = Date.now();
var fileReadTime = 0;
setTimeout(function() {
  var delay = Date.now() - timeoutScheduled;
  console.log(`setTimeout ${delay} ms have passed since I was sheculed`);
  console.log('fileReaderTime', fileReadTime - timeoutScheduled);
}, 10);

someAsyncOperation(function() {
  fileReadtime = Date.now();
  while (Date.now() - fileReadTime < 20) {}
});
  • 執行過程:
    • setTimeout和readFile先後加入io
    • setTimeout執行進入io;(需要10ms)
    • readfile執行,也進入io;(需要2ms)
    • 2ms之後, redfile已經讀取完畢,加入poll隊列,此時poll爲空,執行someAsyncOperation回調;
    • 由於此回調有while,這裏阻塞20ms;執行完爲22ms
    • 在10ms時,setTimeout不能執行,因爲js是單線程,setTimeout一直被阻塞
    • 執行完之後(22ms以後),poll 進入空閒狀態
    • event loop檢查timer,setTimeout回調;
  • 執行結果:
    • readFile執行22ms
    • setTimeout執行22ms之後
  • 執行代碼2
var fs = require('fs');
var path = require('path');
function someAsyncOperation(callback) {
  // 花費9ms
  fs.readFileSync('./read.txt', callback);
}
var timeoutScheduled = Date.now();
var fileReadTime = 0;
setTimeout(function() {
  var delay = Date.now() - timeoutScheduled;
  console.log(`setTimeout ${delay} ms have passed since I was sheculed`);
  console.log('fileReaderTime', fileReadTime - timeoutScheduled);
}, 5);

someAsyncOperation(function() {
  fileReadtime = Date.now();
  while (Date.now() - fileReadTime < 20) {}
});
  • 執行結果
    • setTimeout執行:5ms
    • readFile執行9 ~ 29ms
  • 執行代碼3
  • 在nodejs中,setTimeout(fn,0) === setTimeout(fn,0)
  • 在瀏覽器中,setTimeout(fn,0) === setTimeout(fn,4)
setImmediate(() =>{
    console.log('setImmediate')
},0)

setTimeout(() =>{
    console.log('setTimeout')
},0)

// setTimeout 和 setImmediate的執行順序不確定
// 因爲event loop的啓動也是需要時間的,可能執行到poll階段時已經超過了1ms,此時setTimeout會先執行
const fs = require('fs');
const path = require('path');
fs.readFile(path.resolve(__dirname, '/read.txt'), () => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    console.log('setImmediate');
  }, 0);
});
// 執行順序是確定的, setImmediate,setTimeout
  • 執行過程:
    • 執行fs,第一輪主線程沒有timer和setImmediate
    • 假設readFile需要2ms,執行回調
    • 由poll進入check,setImmediate會先調用
    • 在第二輪的timer階段,會執行setTimeout
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章