NodeJS 是一個異步事件驅動的 JavaScript 運行時環(huán)境,旨在構建可擴展的網絡應用程序。這里的異步是指 JavaScript 中所有在后臺處理而不阻塞任何其他請求的函數。在本文中,您將學習和理解 NodeJS 是如何工作的,以及如何以同步或異步方式處理發(fā)送到服務器的所有功能或請求。
什么是事件循環(huán)?
您可能已經猜對了——Node 在 NodeJS 環(huán)境中使用事件循環(huán)處理請求。但首先,讓我們了解一些有助于我們理解整個機制的基本術語。
事件循環(huán)是一個事件監(jiān)聽器,它在 NodeJS 環(huán)境中運行,并隨時準備監(jiān)聽、處理和輸出事件。
事件可以是從鼠標單擊到按鍵或超時的任何事件。
什么是同步和異步編程?
同步編程意味著代碼按照它定義的順序運行。在同步程序中,當一個函數被調用并返回了某個值時,才會執(zhí)行下一行。
讓我們用這個例子來理解:
const listItems = function(items) {
items.forEach(function(item) {
console.log(item)
})
}
const items = ["Buy milk", "Buy coffee"]
listItems(items)
The output will look like this:
"Buy milk"
"Buy coffee"
在這個例子中,當listItems(items)函數被調用時,它將循環(huán)遍歷項目數組。console.log(item)首先為數組的第一項調用該函數并打印"Buy milk". 然后再次console.log(item)執(zhí)行,這次它傳遞數組的第二項并打印"Buy coffee".
所以你可以說這個函數是按照它定義的順序執(zhí)行的。
另一方面,異步編程指的是不按順序執(zhí)行的代碼。這些功能不是按照它們在程序中定義的順序來執(zhí)行的,而是僅在滿足某些條件時才執(zhí)行的。
例如,setTimeOut()在某個預定義的毫秒數延遲后執(zhí)行任務。
setTimeOut(function(){
return( console.log("Hello World!") )
}, 3000)
這些函數不會逐行運行,而是僅在需要運行時才運行,而不管函數的聲明如何。在這種情況下,當所有同步功能都執(zhí)行完畢后,該功能會在 3 秒后自動運行。
注意:異步函數只有在所有同步函數都執(zhí)行完后才會運行和執(zhí)行。在此之前,它們將在后臺處理。
如果想深入了解NodeJS和異步編程,可以參考這篇文章
但是,NodeJS 如何在后臺處理異步函數并先運行所有同步函數?所有這些機制都可以用 NodeJS 事件循環(huán)輕松解釋。
事件循環(huán)如何工作?
現在讓我們看看 NodeJS 事件循環(huán)如何使用 Nodejs 事件循環(huán)圖執(zhí)行一個簡單的同步程序。然后我們將檢查 Node 如何逐行執(zhí)行程序。
當我們閱讀本節(jié)時,您將開始了解您在此處看到的內容:
在左上角,您有一個要執(zhí)行的 Node 文件。在左下角,您有一個程序的輸出終端。然后你有調用堆棧、節(jié)點 API 和回調隊列。所有這些共同構成了 NodeJS 環(huán)境。
對于同步編程,您只需要關注調用堆棧。這是 NodeJS 環(huán)境中唯一可以在這種情況下工作的部分。
回調堆棧是一種數據結構,用于跟蹤將在程序內部運行的所有函數的執(zhí)行情況。這種數據結構只有一個開放端來添加或刪除頂級項目。
當程序開始執(zhí)行時,它首先被包裹在一個匿名main()函數中。這是由 NodeJS 自動定義的。所以main()首先被推送到回調堆棧。
接下來,創(chuàng)建變量a和b并將它們的總和存儲在變量中sum。所有這些值都存儲在內存中。
現在,這console.log()是一個被調用并推送到回調堆棧中的函數。它被執(zhí)行,你可以在終端屏幕上看到輸出。
執(zhí)行此函數后,它將從回調堆棧中刪除。然后main()也被刪除,因為程序中沒有任何東西可以調用。這就是同步程序的執(zhí)行方式。
現在,讓我們看看異步函數或程序如何在 NodeJS 中執(zhí)行。我們需要回調堆棧、Node API 和回調隊列一起來處理異步函數。
讓我們從這個例子開始:
像往常一樣,當程序開始執(zhí)行時,首先將main()函數添加到回調堆棧中。然后console.log("Start")被調用并添加到回調堆棧中。處理后,輸出在終端上可見,然后從回調堆棧中刪除。
現在下一個是setTimeOut(...Zero...)添加到回調堆棧中的函數。
由于這是一個異步函數,它不會在回調堆棧中得到處理。然后它從回調堆棧添加到節(jié)點 API,在那里注冊事件并設置回調函數以在后臺處理。
接下來是setTimeOut(...Two..)從回調堆棧添加到 Node API 的 ,因為它是一個異步函數。然后另一個回調函數被設置為在后臺超時 2 秒后處理。到此為止,可以執(zhí)行其他功能。
這被稱為非阻塞行為,所有同步函數首先被處理和執(zhí)行,異步函數在后臺處理,同時等待輪到它們被執(zhí)行。
接下來,該console.log("End")函數最后在回調堆棧中被調用并在此處進行處理。您可以在終端上看到輸出?,F在,所有同步函數都被處理,并main()從回調堆棧中刪除。
在后臺,所有異步函數都得到處理,它們的回調存儲在回調隊列中。首先處理的將首先添加到隊列中以在回調堆棧中執(zhí)行。
注意:異步函數不能在回調堆棧內運行,直到它被清空。這意味著main()從調用堆棧中刪除后,所有異步函數才能開始執(zhí)行。
現在,使用事件循環(huán)將它們一一推送到回調堆棧并最終執(zhí)行。每個回調函數將打印console.log()每次調用該函數的值。
最后,這些在執(zhí)行后也被刪除,現在回調堆棧為空。
這就是 NodeJS 如何在環(huán)境中執(zhí)行同步和異步函數,以及事件循環(huán)如何調用異步函數。
結論
在本文中,您了解了 NodeJS 的內部工作原理,并了解了異步程序是如何執(zhí)行的。
現在您應該明白為什么兩秒延遲函數不會阻止程序的其余部分執(zhí)行。您也知道為什么零秒延遲函數在“End”打印后最后打印該值。
就這樣!我希望你喜歡閱讀這篇文章并學到一些新東西。如果您覺得這篇文章有用,請分享它。