?譯者 | 盧鑫旺
當我們啟動一個Node.js應(yīng)用程序時,它會加載事件循環(huán)并將必要的回調(diào)函數(shù)添加到回調(diào)棧中。在本文中,你將詳細了解Node.js中的事件循環(huán)是如何工作的。
一、為什么你應(yīng)該了解Node.js中的事件循環(huán)
以下幾點闡述為什么了解事件循環(huán)很重要:
理解事件循環(huán)的原理有助于你寫出更高效的代碼
當你的應(yīng)用程序出現(xiàn)異常錯誤時能幫你更容易地調(diào)試你的代碼
二、什么是事件循環(huán)
根據(jù)Node.js官方網(wǎng)站的說法,事件循環(huán)允許Node.js執(zhí)行非阻塞I/O操作——盡管JavaScript是單線程的——會盡可能地將操作轉(zhuǎn)移到系統(tǒng)內(nèi)核中。
我們可以把這個定義分解為三個關(guān)鍵字:
- 非阻塞I/O操作
- 單線程
- 系統(tǒng)內(nèi)核
1.非阻塞I/O操作
如果一個操作的執(zhí)行沒有被阻塞,我們就說這個程序是非阻塞的。既然我們在這里提到了非阻塞,那么我們也應(yīng)該提到什么是阻塞。它只是意味著你必須在一個操作完成后才能再完成另一個操作。
2.單線程
如果一個程序只有一個調(diào)用棧,并且它使用了先進先出的概念,在同一個時刻只能執(zhí)行一個任務(wù),那么這個程序就是單線程的。這意味著棧上的第一個程序總是在下一個程序之前運行。雖然JavaScript看起來是單線程的語言,不過這只取決于它運行的環(huán)境。
3.系統(tǒng)內(nèi)核
在這里,內(nèi)核只是指運行程序的操作系統(tǒng)。Javascript是單線程的,但Node.js在執(zhí)行多個輸入輸出(I/O)操作時能夠不阻塞線程。它通過盡可能將此操作交給操作系統(tǒng)(例如Linux、Windows、Mac OS X等)來實現(xiàn)這一點。操作大多被轉(zhuǎn)移到操作系統(tǒng)中;這就是Javascript與Node.js的區(qū)別。
三、Node.js中的事件循環(huán)是如何工作的
當我們啟動node應(yīng)用程序時,事件循環(huán)立即開始運行。事件循環(huán)有多個階段,每個階段都有要執(zhí)行的回調(diào)隊列。當事件循環(huán)被添加到特定階段時,它將在該特定階段執(zhí)行一些操作,然后在該階段隊列中執(zhí)行一些回調(diào)。
這將一直持續(xù)到隊列為空或已經(jīng)執(zhí)行了最大數(shù)量的回調(diào)函數(shù)。當達到限制時,事件循環(huán)會進入下一階段執(zhí)行相同的操作。
有四個最重要的階段:
- 到期時間回調(diào)
- I/O輪詢和回調(diào)
- setImmediate回調(diào)
- close回調(diào)
1.到期時間回調(diào)
該階段負責處理過期定時器的回調(diào)函數(shù)。
舉例:
是一個函數(shù),它設(shè)置了一個在一定時間后過期的定時器。
因此,如果這個定時器有回調(diào)函數(shù)的話,那么它們將是事件循環(huán)首先處理的函數(shù)。
如果計時器稍后到期,在處理其他某個階段的時間內(nèi),則只有當事件循環(huán)返回到第一階段時,才會調(diào)用該計時器的回調(diào)。它在所有四個階段都是這樣工作的。
2.I/O輪詢和回調(diào)
輪詢基本上意味著搜索準備好處理的新I/O事件,并將其放入回調(diào)隊列。理解在Node應(yīng)用程序的上下文中,I/O只是指網(wǎng)絡(luò)和文件訪問之類的事情,這一點至關(guān)重要。
舉例:
在這個階段,99%的代碼都會被執(zhí)行,因為在典型的Node應(yīng)用程序中,我們需要做的大部分工作都與網(wǎng)絡(luò)和讀取文件有關(guān)。
3.setImmediate回調(diào)
如果我們想在輪詢和執(zhí)行階段的I/O之后立即處理回調(diào),我們使用這個特殊的計時器。這在一些更高級的情況下可能是重要的。
4.close回調(diào)
在這個階段,所有的close關(guān)閉事件都會被處理,例如,當一個Web服務(wù)器關(guān)閉時。這就完成了事件循環(huán)的第四個階段。
注意:Node.js事件循環(huán)內(nèi)部還使用了其他事件,但就本文而言,以上四個事件對我們來說是至關(guān)重要的。
如上所述,我們完成了這個過程,這只是事件循環(huán)中的一個周期。循環(huán)結(jié)束后,Node.js決定是繼續(xù)循環(huán)還是退出循環(huán)。
Node只是通過檢查是否有任務(wù)(例如定時器或I/0任務(wù))仍然在后臺運行來做到這一點。如果沒有,它就會退出應(yīng)用程序。如果有待處理的任務(wù),它們會繼續(xù)處理下一個任務(wù),例如處理HTTP請求或讀取文件。
這基本上就是Node事件循環(huán)的全部內(nèi)容。
四、避免阻塞事件循環(huán)
因為Node.js中的所有內(nèi)容最終都運行在單個線程中,你可以讓數(shù)百萬用戶同時訪問同一個池,這使得Nde.js非常輕量級和可擴展。但與此同時,它有阻塞單個線程的危險,這將使整個應(yīng)用程序變慢,甚至停止應(yīng)用程序。
五、避免阻塞事件循環(huán)的一些準則
作為開發(fā)人員,你有責任避免事件循環(huán)的阻塞。下面的這些準則可以幫助你避免阻塞事件循環(huán)。
- 不要在回調(diào)函數(shù)中使用fs、crypto和Zlib模塊中函數(shù)的同步版本。
- 處理較大的對象時,使用JSON時要小心。
- 不要使用過于復(fù)雜的正則表達式(例如,嵌套的量詞)。
- 不要在嵌套對象上執(zhí)行復(fù)雜的計算。
六、關(guān)于Node.js事件循環(huán)的最后思考
事件循環(huán)使Node.js中的異步編程成為可能。這使得它成為Node設(shè)計中最重要的特性。這使得Node.js與其他平臺完全不同。
它負責處理所有傳入的事件,并通過將較重的任務(wù)轉(zhuǎn)到線程池并自己執(zhí)行最簡單的工作來執(zhí)行編排。
原文鏈接:https://hackernoon.com/how-do-event-loops-in-nodejs-work
譯者介紹
盧鑫旺,51CTO社區(qū)編輯,編程語言愛好者,對數(shù)據(jù)庫,架構(gòu),云原生有濃厚興趣。?