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