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