摘要:由此可以認(rèn)為,實(shí)際上是一系列的回調(diào)函數(shù)集合。此時(shí)瀏覽器會(huì)在其它線程中執(zhí)行異步操作,操作完成后將回調(diào)函數(shù)放入主線程任務(wù)隊(duì)列中。當(dāng)主線程將執(zhí)行棧中的函數(shù)執(zhí)行完畢后,再次讀取任務(wù)隊(duì)列,形成循環(huán)。
先看一段代碼,也是一道經(jīng)典面試題:
(function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })()
其輸出結(jié)果為:
// 1 // 2 // 3 // 5 // 4
我們知道,JavaScript 在同一時(shí)間片內(nèi)只能執(zhí)行一個(gè)任務(wù):
主線程會(huì)依次執(zhí)行代碼,當(dāng)執(zhí)行到函數(shù)的時(shí)候會(huì)將函數(shù)加入執(zhí)行棧,當(dāng)函數(shù)執(zhí)行完畢后再將其出棧,直至代碼執(zhí)行完畢。當(dāng)執(zhí)行棧為空時(shí),runtime 會(huì)從任務(wù)隊(duì)列(先入先出)中取出待執(zhí)行的回調(diào)函數(shù)并執(zhí)行,入棧、出棧的過(guò)程同上。這個(gè)機(jī)制就叫做 Event Loop。
由此可以認(rèn)為,Event Loop實(shí)際上是一系列的回調(diào)函數(shù)集合。
舉例來(lái)說(shuō):在瀏覽器中,對(duì)于網(wǎng)絡(luò)請(qǐng)求等需要等待一段時(shí)間才會(huì)返回結(jié)果的操作,我們通常采用異步回調(diào)來(lái)處理,這個(gè)回調(diào)就會(huì)放入任務(wù)隊(duì)列中。此時(shí)瀏覽器會(huì)在其它線程中執(zhí)行異步操作,操作完成后將回調(diào)函數(shù)放入主線程任務(wù)隊(duì)列中。Event Loop負(fù)責(zé)在主線程執(zhí)行完畢后將任務(wù)隊(duì)列中的函數(shù)放入執(zhí)行棧中,由主線程執(zhí)行。當(dāng)主線程將執(zhí)行棧中的函數(shù)執(zhí)行完畢后,再次讀取任務(wù)隊(duì)列,形成循環(huán)。所以即使主線程阻塞了,任務(wù)隊(duì)列依然能夠被添加函數(shù),因?yàn)槿蝿?wù)隊(duì)列的添加是由瀏覽器負(fù)責(zé)的。(不同的 runtime 實(shí)現(xiàn)可能不同)
另外需要注意的是 Promise.then 是異步執(zhí)行的,而創(chuàng)建 Promise 實(shí)例是同步執(zhí)行的。這就解釋了為什么1、2、3輸出在4、5之前。
但為什么5 會(huì)輸出在4前面呢?
JavaScript 中的任務(wù)又分為MacroTask 與 MicroTask 兩種。
典型的 MacroTask 包含了 :
setTimeout
setInterval
setImmediate
requestAnimationFrame
I/O
UI rendering
而常見(jiàn)的MicroTask 包含了
process.nextTick
Promises
Object.observe
MutationObserver
Event Loop 中有一個(gè)或多個(gè)Task Queue,即MacroTask Queue,僅有一個(gè)Job Queue,即MicroTask Queue。Task Queue的執(zhí)行是按照回調(diào)順序先入先出,而在 MacroTask 的執(zhí)行間隙中會(huì)清空已有的 MicroTask Queue
回到代碼中,setTimeout(function() {console.log(4)}, 0); 既然延遲設(shè)置為0,為什么5會(huì)在4之前輸入呢?
那是因?yàn)閟etTimeout設(shè)置為0的時(shí)候,runtime其實(shí)并不是0,在主流瀏覽器中會(huì)將其設(shè)置為4,而 node 則會(huì)將其設(shè)置為1。那么現(xiàn)在代碼的執(zhí)行順序就很清晰了:
console.log(1); // 創(chuàng)建 Promise 主線程執(zhí)行 ... console.log(2); // 創(chuàng)建 Promise 主線程執(zhí)行 console.log(3); // test 函數(shù)立即執(zhí)行, 主線程執(zhí)行 ... console.log(5); // 主線程執(zhí)行完畢,執(zhí)行MicroTask Queue,即 promise.then ... console.log(4); // 執(zhí)行 setTimeout(4)
參考資料:
https://developer.mozilla.org...
https://html.spec.whatwg.org/...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94529.html
摘要:如果當(dāng)前沒(méi)有事件也沒(méi)有定時(shí)器事件,則返回。相關(guān)資料關(guān)于的架構(gòu)及設(shè)計(jì)思路的事件討論了使用線程池異步運(yùn)行代碼。下一篇初窺事件機(jī)制的實(shí)現(xiàn)二中定時(shí)器的實(shí)現(xiàn) 在瀏覽器中,事件作為一個(gè)極為重要的機(jī)制,給予JavaScript響應(yīng)用戶(hù)操作與DOM變化的能力;在Node.js中,事件驅(qū)動(dòng)模型則是其高并發(fā)能力的基礎(chǔ)。 學(xué)習(xí)JavaScript也需要了解它的運(yùn)行平臺(tái),為了更好的理解JavaScript的事...
摘要:但是導(dǎo)致了很明顯的性能問(wèn)題。上述兩個(gè)例子其實(shí)是在這個(gè)中找到的,第一個(gè)使用的版本是,這個(gè)版本的實(shí)現(xiàn)是采用了,而后因?yàn)榈睦锏挠?,于是尤雨溪更改了?shí)現(xiàn),換成了,也就是后一個(gè)所使用的。后來(lái)尤雨溪了解到是將回調(diào)放入的隊(duì)列。 結(jié)論 對(duì)于event loop 可以抽象成一段簡(jiǎn)單的代碼表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:但是導(dǎo)致了很明顯的性能問(wèn)題。上述兩個(gè)例子其實(shí)是在這個(gè)中找到的,第一個(gè)使用的版本是,這個(gè)版本的實(shí)現(xiàn)是采用了,而后因?yàn)榈睦锏挠?,于是尤雨溪更改了?shí)現(xiàn),換成了,也就是后一個(gè)所使用的。后來(lái)尤雨溪了解到是將回調(diào)放入的隊(duì)列。 結(jié)論 對(duì)于event loop 可以抽象成一段簡(jiǎn)單的代碼表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過(guò)任務(wù)隊(duì)列的機(jī)制來(lái)進(jìn)行協(xié)調(diào)的。等便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當(dāng)前本輪的結(jié)束,主線程可以繼續(xù)取下一個(gè)執(zhí)行。 依然是:經(jīng)濟(jì)基礎(chǔ)決定上層建筑。 說(shuō)明 首先,旨在搞清常用的同步異步執(zhí)行機(jī)制 其次,暫時(shí)不討論node.js的Event Loop執(zhí)行機(jī)制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機(jī)制 最后,借鑒了很多前輩的研究文...
摘要:前言前幾天在理解的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里的好奇。接下來(lái)理解瀏覽器中的,先看一張圖堆和棧堆是用戶(hù)主動(dòng)請(qǐng)求而劃分出來(lái)的內(nèi)存區(qū)域,比如你,就是將一個(gè)對(duì)象存入堆中,可以理解為存對(duì)象。廢話不多說(shuō),直接上圖個(gè)人理解。參考資料運(yùn)行機(jī)制詳解再談 前言 前幾天在理解node的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里Event Loop的好奇。我們都知道javascript是單線程的,任務(wù)是需要一個(gè)一個(gè)按順...
摘要:曾經(jīng)的理解首先,是單線程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)節(jié)點(diǎn)上編輯了內(nèi)容,而另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器就很懵逼了,到底以執(zhí)行哪個(gè)操作呢所以,設(shè)計(jì)者把 Event Loop曾經(jīng)的理解 首先,JS是單線程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...
閱讀 3133·2023-04-26 00:40
閱讀 2495·2021-09-27 13:47
閱讀 4517·2021-09-07 10:22
閱讀 3029·2021-09-06 15:02
閱讀 3379·2021-09-04 16:45
閱讀 2574·2021-08-11 10:23
閱讀 3665·2021-07-26 23:38
閱讀 2958·2019-08-30 15:54