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