App下載

NodeJS 中的事件循環(huán)——同步和異步代碼初學(xué)者指南

萌夠才回家 2021-08-31 10:20:33 瀏覽數(shù) (3486)
反饋

NodeJS 是一個(gè)異步事件驅(qū)動(dòng)的 JavaScript 運(yùn)行時(shí)環(huán)境,旨在構(gòu)建可擴(kuò)展的網(wǎng)絡(luò)應(yīng)用程序。這里的異步是指 JavaScript 中所有在后臺(tái)處理而不阻塞任何其他請(qǐng)求的函數(shù)。在本文中,您將學(xué)習(xí)和理解 NodeJS 是如何工作的,以及如何以同步或異步方式處理發(fā)送到服務(wù)器的所有功能或請(qǐng)求。

什么是事件循環(huán)?

您可能已經(jīng)猜對(duì)了——Node 在 NodeJS 環(huán)境中使用事件循環(huán)處理請(qǐng)求。但首先,讓我們了解一些有助于我們理解整個(gè)機(jī)制的基本術(shù)語(yǔ)。

事件循環(huán)是一個(gè)事件監(jiān)聽(tīng)器,它在 NodeJS 環(huán)境中運(yùn)行,并隨時(shí)準(zhǔn)備監(jiān)聽(tīng)、處理和輸出事件。

事件可以是從鼠標(biāo)單擊到按鍵或超時(shí)的任何事件。

什么是同步和異步編程?

同步編程意味著代碼按照它定義的順序運(yùn)行。在同步程序中,當(dāng)一個(gè)函數(shù)被調(diào)用并返回了某個(gè)值時(shí),才會(huì)執(zhí)行下一行。

讓我們用這個(gè)例子來(lái)理解:

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".

所以你可以說(shuō)這個(gè)函數(shù)是按照它定義的順序執(zhí)行的。

另一方面,異步編程指的是不按順序執(zhí)行的代碼。這些功能不是按照它們?cè)诔绦蛑卸x的順序來(lái)執(zhí)行的,而是僅在滿(mǎn)足某些條件時(shí)才執(zhí)行的。

例如,setTimeOut()在某個(gè)預(yù)定義的毫秒數(shù)延遲后執(zhí)行任務(wù)。

setTimeOut(function(){
    return( console.log("Hello World!") )
}, 3000)

這些函數(shù)不會(huì)逐行運(yùn)行,而是僅在需要運(yùn)行時(shí)才運(yùn)行,而不管函數(shù)的聲明如何。在這種情況下,當(dāng)所有同步功能都執(zhí)行完畢后,該功能會(huì)在 3 秒后自動(dòng)運(yùn)行。

注意:異步函數(shù)只有在所有同步函數(shù)都執(zhí)行完后才會(huì)運(yùn)行和執(zhí)行。在此之前,它們將在后臺(tái)處理。

如果想深入了解NodeJS和異步編程,可以參考這篇文章

但是,NodeJS 如何在后臺(tái)處理異步函數(shù)并先運(yùn)行所有同步函數(shù)?所有這些機(jī)制都可以用 NodeJS 事件循環(huán)輕松解釋。

事件循環(huán)如何工作?

現(xiàn)在讓我們看看 NodeJS 事件循環(huán)如何使用 Nodejs 事件循環(huán)圖執(zhí)行一個(gè)簡(jiǎn)單的同步程序。然后我們將檢查 Node 如何逐行執(zhí)行程序。

當(dāng)我們閱讀本節(jié)時(shí),您將開(kāi)始了解您在此處看到的內(nèi)容:1

在左上角,您有一個(gè)要執(zhí)行的 Node 文件。在左下角,您有一個(gè)程序的輸出終端。然后你有調(diào)用堆棧、節(jié)點(diǎn) API 和回調(diào)隊(duì)列。所有這些共同構(gòu)成了 NodeJS 環(huán)境。

對(duì)于同步編程,您只需要關(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è)開(kāi)放端來(lái)添加或刪除頂級(jí)項(xiàng)目。

當(dāng)程序開(kāi)始執(zhí)行時(shí),它首先被包裹在一個(gè)匿名main()函數(shù)中。這是由 NodeJS 自動(dòng)定義的。所以main()首先被推送到回調(diào)堆棧。

2

接下來(lái),創(chuàng)建變量a和b并將它們的總和存儲(chǔ)在變量中sum。所有這些值都存儲(chǔ)在內(nèi)存中。

現(xiàn)在,這console.log()是一個(gè)被調(diào)用并推送到回調(diào)堆棧中的函數(shù)。它被執(zhí)行,你可以在終端屏幕上看到輸出。

3

執(zhí)行此函數(shù)后,它將從回調(diào)堆棧中刪除。然后main()也被刪除,因?yàn)槌绦蛑袥](méi)有任何東西可以調(diào)用。這就是同步程序的執(zhí)行方式。

45

現(xiàn)在,讓我們看看異步函數(shù)或程序如何在 NodeJS 中執(zhí)行。我們需要回調(diào)堆棧、Node API 和回調(diào)隊(duì)列一起來(lái)處理異步函數(shù)。

讓我們從這個(gè)例子開(kāi)始:

1-1

像往常一樣,當(dāng)程序開(kāi)始執(zhí)行時(shí),首先將main()函數(shù)添加到回調(diào)堆棧中。然后console.log("Start")被調(diào)用并添加到回調(diào)堆棧中。處理后,輸出在終端上可見(jiàn),然后從回調(diào)堆棧中刪除。

2-13-1

現(xiàn)在下一個(gè)是setTimeOut(...Zero...)添加到回調(diào)堆棧中的函數(shù)。

由于這是一個(gè)異步函數(shù),它不會(huì)在回調(diào)堆棧中得到處理。然后它從回調(diào)堆棧添加到節(jié)點(diǎn) API,在那里注冊(cè)事件并設(shè)置回調(diào)函數(shù)以在后臺(tái)處理。

4-15-1

接下來(lái)是setTimeOut(...Two..)從回調(diào)堆棧添加到 Node API 的 ,因?yàn)樗且粋€(gè)異步函數(shù)。然后另一個(gè)回調(diào)函數(shù)被設(shè)置為在后臺(tái)超時(shí) 2 秒后處理。到此為止,可以執(zhí)行其他功能。

這被稱(chēng)為非阻塞行為,所有同步函數(shù)首先被處理和執(zhí)行,異步函數(shù)在后臺(tái)處理,同時(shí)等待輪到它們被執(zhí)行。

67

接下來(lái),該console.log("End")函數(shù)最后在回調(diào)堆棧中被調(diào)用并在此處進(jìn)行處理。您可以在終端上看到輸出。現(xiàn)在,所有同步函數(shù)都被處理,并main()從回調(diào)堆棧中刪除。

在后臺(tái),所有異步函數(shù)都得到處理,它們的回調(diào)存儲(chǔ)在回調(diào)隊(duì)列中。首先處理的將首先添加到隊(duì)列中以在回調(diào)堆棧中執(zhí)行。

8910

注意:異步函數(shù)不能在回調(diào)堆棧內(nèi)運(yùn)行,直到它被清空。這意味著main()從調(diào)用堆棧中刪除后,所有異步函數(shù)才能開(kāi)始執(zhí)行。

現(xiàn)在,使用事件循環(huán)將它們一一推送到回調(diào)堆棧并最終執(zhí)行。每個(gè)回調(diào)函數(shù)將打印console.log()每次調(diào)用該函數(shù)的值。

11

最后,這些在執(zhí)行后也被刪除,現(xiàn)在回調(diào)堆棧為空。

12

這就是 NodeJS 如何在環(huán)境中執(zhí)行同步和異步函數(shù),以及事件循環(huán)如何調(diào)用異步函數(shù)。

結(jié)論

在本文中,您了解了 NodeJS 的內(nèi)部工作原理,并了解了異步程序是如何執(zhí)行的。

現(xiàn)在您應(yīng)該明白為什么兩秒延遲函數(shù)不會(huì)阻止程序的其余部分執(zhí)行。您也知道為什么零秒延遲函數(shù)在“End”打印后最后打印該值。

就這樣!我希望你喜歡閱讀這篇文章并學(xué)到一些新東西。如果您覺(jué)得這篇文章有用,請(qǐng)分享它。


0 人點(diǎn)贊