事件循環(huán)是用來理解JavaScript的最重要的方面之一。這篇文章旨在解釋JavaScript如何與單個(gè)線程一起工作的細(xì)節(jié),以及它如何處理異步函數(shù)。
JavaScript代碼運(yùn)行是單線程。一次只執(zhí)行一件事。這實(shí)際上是一個(gè)非常有用的限制,因?yàn)樗?jiǎn)化了很多程序,從而不必?fù)?dān)心并發(fā)問題。
您只需要注意編寫代碼的方式,避免任何可能阻塞線程的內(nèi)容,如同步調(diào)用或無(wú)限循環(huán)。
通常,在大多數(shù)瀏覽器中,每個(gè)瀏覽器都有一個(gè)事件循環(huán),以使每個(gè)進(jìn)程隔離,并避免web頁(yè)面具有無(wú)限循環(huán)或繁重的處理來阻塞整個(gè)瀏覽器。
你最需要擔(dān)心的是,您的代碼將在單個(gè)事件循環(huán)上運(yùn)行,并在編寫代碼時(shí)考慮到這一點(diǎn),以避免阻塞它。
阻止事件循環(huán)
任何花費(fèi)太長(zhǎng)時(shí)間將控制權(quán)返回給事件循環(huán)的JavaScript代碼都會(huì)阻止頁(yè)面中任何JavaScript代碼的執(zhí)行,甚至阻止UI線程,用戶也無(wú)法點(diǎn)擊,滾動(dòng)頁(yè)面等等。
幾乎所有JavaScript中的I / O操作都是非阻塞的。網(wǎng)絡(luò)請(qǐng)求,Node.js文件系統(tǒng)操作等。阻塞是個(gè)例外,這就是為什么JavaScript基于回調(diào),以及最近的promises和async / await。
調(diào)用堆棧
調(diào)用堆棧是LIFO隊(duì)列(Last In,F(xiàn)irstOut)。事件循環(huán)不斷檢查調(diào)用堆棧以查看是否存在需要運(yùn)行的任何函數(shù)。
在執(zhí)行此操作時(shí),它會(huì)將它找到的任何函數(shù)調(diào)用添加到調(diào)用堆棧并按順序執(zhí)行每個(gè)調(diào)用。
一個(gè)簡(jiǎn)單的事件循環(huán)說明:
當(dāng)此代碼運(yùn)行時(shí),首先f(wàn)oo()調(diào)用。在foo()我們第一次調(diào)用bar(),然后我們調(diào)用baz()。
排隊(duì)功能執(zhí)行
上面的例子運(yùn)行特點(diǎn):JavaScript找到要執(zhí)行的東西,按順序運(yùn)行它們。
讓我們看看如何推遲函數(shù)直到堆棧清除:
用例setTimeout(()=> {}), 0)是調(diào)用一個(gè)函數(shù),但是一旦執(zhí)行了代碼中的每個(gè)其他函數(shù)就執(zhí)行它。
當(dāng)此代碼運(yùn)行時(shí),首先調(diào)用foo()。在foo()里面我們首先調(diào)用setTimeout,bar作為參數(shù)傳遞,然后我們指示它盡可能快地運(yùn)行,將0作為計(jì)時(shí)器傳遞。然后我們調(diào)用baz()。
消息隊(duì)列
調(diào)用setTimeout()時(shí),瀏覽器或Node.js啟動(dòng)計(jì)時(shí)器。當(dāng)計(jì)時(shí)器到期,我們將0作為超時(shí),回調(diào)函數(shù)立即被放入消息隊(duì)列中。
消息隊(duì)列也是用戶發(fā)起的事件(如單擊事件、鍵盤事件或獲取響應(yīng))在代碼有機(jī)會(huì)對(duì)其作出響應(yīng)之前排隊(duì)的地方。或者像onLoad這樣的DOM事件。
循環(huán)優(yōu)先處理調(diào)用堆棧,它首先處理在調(diào)用堆棧中找到的所有東西,一旦調(diào)用堆棧中沒有任何東西,它就會(huì)去獲取事件隊(duì)列中的東西。
我們不必等待像setTimeout,fetch或其他東西這樣的函數(shù)來完成自己的工作,因?yàn)樗鼈兪怯蔀g覽器提供的,并且它們運(yùn)行在自己的線程中。
ES6作業(yè)隊(duì)列
ECMAScript 2015引入了Promises使用的作業(yè)隊(duì)列概念(也在ES6 / ES2015中引入)。這是一種盡快執(zhí)行異步函數(shù)結(jié)果的方法,而不是放在調(diào)用堆棧的末尾。
在當(dāng)前函數(shù)結(jié)束之前解析的Prom將在當(dāng)前函數(shù)之后立即執(zhí)行。
我覺得在游樂園里過山車的比喻很好:消息隊(duì)列將你放在隊(duì)列的后面,在所有其他人的后面,你將不得不等待輪到你,而作業(yè)隊(duì)列是快速通票這可以讓你在完成上一個(gè)之后再騎一次。
這是Promises(和Async / await,它建立在promises上)和普通的舊異步函數(shù)setTimeout()或其他平臺(tái)API 之間的巨大差異。
javascrit的事件循環(huán)是這門語(yǔ)言中非常重要且基礎(chǔ)的概念。清楚的了解了事件循環(huán)的執(zhí)行順序和每一個(gè)階段的特點(diǎn),可以使我們對(duì)一段異步代碼的執(zhí)行順序有一個(gè)清晰的認(rèn)識(shí),從而減少代碼運(yùn)行的不確定性。合理的使用各種延遲事件的方法,有助于代碼更好的按照其優(yōu)先級(jí)去執(zhí)行。
-
Web
+關(guān)注
關(guān)注
2文章
1269瀏覽量
69623 -
javascript
+關(guān)注
關(guān)注
0文章
525瀏覽量
53909
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論