【譯】Understanding NodeJS Event Loop

蒜薹兒發表於2019-01-23

原文連結Understanding NodeJS Event Loop

Event Loop是個讓人頭大的知識點,今天我們先了解下Nodejs中的Event Loop(不要跟瀏覽器中的Event Loop混淆)。 在這篇文章中,作者將證明Node不完全是單執行緒的,並向你展示事件迴圈的一般實現。

快速瀏覽非阻塞I / O.

思考下這段程式碼:

const crypto = require('crypto')

const start = Date.now()

crypto.pbkdf2('secret', 'salt', 100000, 512, 'sha512', () => {
  console.log('1:', Date.now() - start)
}) //1: 1015

crypto.pbkdf2('secret', 'salt', 100000, 512, 'sha512', () => {
  console.log('2:', Date.now() - start)
}) //2: 1021

crypto.pbkdf2('secret', 'salt', 100000, 512, 'sha512', () => {
  console.log('3:', Date.now() - start)
}) //3: 1017
複製程式碼

當執行此程式碼時,所有函式呼叫都進入事件迴圈。執行程式碼之後,您會發現這3個函式呼叫所花費的時間幾乎相同。還記得事件迴圈在一個執行緒中嗎?Node是如何在一個執行緒內並行執行3個操作的?

Node基於C / C ++構建

我們上面觀察到的行為是因為Node實現了一個名為libuv的C模組。每當在事件迴圈中發生長時間執行的操作時,libuv都會將該任務放入另一個執行緒中。操作結束後,事件迴圈將觸發回撥去處理結果。

現在,將crypto.pbkdf2()函式的呼叫次數增加到5次,看看會發生什麼。

您會發現前4個執行的時間幾乎相同,而最後一個執行則需要兩倍的時間。在這裡,我們遇到了一個Node的有趣部分。預設情況下,libuv庫在稱為“執行緒池”的東西中啟動4個執行緒。並且這4個執行緒是並行執行的,這就是為什麼4個操作的時間是相同的。因為執行緒池一次只能執行4次操作,所以第五次操作只是等待先前的操作執行完成。這就是第五次操作需要更長時間的原因。

現在,我們來改變操作。這次我們將並行進行http呼叫,看看會發生什麼。

const https = require('https')

const start = Date.now()

function makeRequest() {
  https
    .request('https://www.baidu.com', res => {
      res.on('data', () => {})
      res.on('end', () => {
        console.log(Date.now() - start)
      })
    })
    .end()
}

makeRequest() //67
makeRequest() //72
makeRequest() //72
makeRequest() //73
makeRequest() //74
複製程式碼

你會發現這5個的執行時間幾乎相同。我們可以合理地推斷出這些http呼叫既不會發生在單執行緒事件迴圈中,也不會發生在執行緒池中。所以發生了什麼事?

事實證明,一些底層任務(如http請求)被libuv委託給作業系統。這些任務在事件迴圈執行緒池之外並行執行。

事件迴圈還處理計時器事件,即setTimeoutsetIntervalsetImmediate。註冊計時器事件時,事件迴圈將等待指定的時間並觸發回撥。

總之,事件迴圈歸結為這三種事件:

1.計時器事件,即setTimeoutsetIntervalsetImmediate

2.作業系統任務,例如伺服器監聽和http請求。

3.長時間執行的操作,例如fs操作和其他Node API

這是一張圖表,用以說明:

【譯】Understanding NodeJS Event Loop

感謝@serialcoder 大佬的指導。

相關文章