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
委託給作業系統。這些任務在事件迴圈
和執行緒池
之外並行執行。
事件迴圈
還處理計時器事件,即setTimeout
,setInterval
和setImmediate
。註冊計時器事件時,事件迴圈
將等待指定的時間並觸發回撥。
總之,事件迴圈
歸結為這三種事件:
1.計時器事件,即setTimeout
,setInterval
和setImmediate
。
2.作業系統任務,例如伺服器監聽和http
請求。
3.長時間執行的操作,例如fs操作
和其他Node API
。
這是一張圖表,用以說明:
感謝@serialcoder 大佬的指導。