摘要:心塞塞根據規范,事件循環是通過任務隊列的機制來進行協調的。等便是任務源,而進入任務隊列的是他們指定的具體執行任務回調函數。然后當前本輪的結束,主線程可以繼續取下一個執行。
依然是:經濟基礎決定上層建筑。
說明首先,旨在搞清常用的同步異步執行機制
其次,暫時不討論node.js的Event Loop執行機制,以下關于瀏覽器的Event Loop執行機制
最后,借鑒了很多前輩的研究文章,非常感謝,此文主要是梳理所學,還請保持質疑以追求正確的知識
要點基本概念
同步異步操作
Event Loop
基本概念先解釋現代js引擎幾個概念。
stack(棧):這里放著js正在執行的任務。理解事件循環一(淺析)一文有對 stack 的 example 解釋。
heap(堆):一個用來表示內存中一大片非結構化區域的名字,對象都被分配在這。
queue(隊列):一個 js runtime 包含了一個任務隊列,該隊列是由一系列待處理的任務組成。而每個任務都有相對應的函數。當棧為空時,就會從任務隊列中取出一個任務,并處理之。當該任務處理完畢后,棧就會再次為空。(queue的特點是先進先出(FIFO))。
為了方便描述與理解,作出以下約定:
stack 棧為主線程
queue 隊列為任務隊列(等待調度到主線程執行)
同步異步js 是一門單線程語言。 js 引擎有一個主線程(main thread)用來解釋和執行 js 程序,實際上還存在其他的線程。例如:處理AJAX請求的線程、處理DOM事件的線程、定時器線程、讀寫文件的線程(例如在node.js中)等等。這些線程可能存在于 js 引擎之內,也可能存在于 js 引擎之外,在此我們不做區分。不妨叫它們工作線程。但是前輩們頗有一種小本本記好的說法,那就是,要相信 js 單線程的本質,其他一切看似多線程,都是紙老虎。哈哈哈哈哈哈哈哈哈哈哈哈哈......
任務分為同步任務(synchronous)和異步任務(asynchronous),如果所有任務都由主線程來處理,會出現主線程被阻塞而使得頁面“假死”。為了主線程不被阻塞,異步任務(如:AJAX異步請求,定時器等)就會交給工作線程來處理,異步任務完成后將異步回調函數注冊進任務隊列,等待主線程空閑時調用。流程如圖:
// example console.log("example-start") setTimeout(() => { console.log("setTimeout-0") }, 0) console.log("example-end") /* chrome result * example-start example-end setTimeout-0 * */
上面一個簡單的小 js 片段的執行過程:
主線程開始同步任務執行,執行console.log("example-start")
然后接下來,主線程遇見一個異步操作setTimeout,將改異步任務交給工作線程處理,異步任務完成之后,將回調函數注冊進任務隊列,等待被調用
繼續同步任務處理,執行console.log("example-end")
主線程空閑,調用任務隊列中等待執行的回調函數,執行console.log("setTimeout-0")
最后借用Philip Roberts的生動形象的一張圖,callback queue可以簡單理解為任務隊列,詳細的下面會講。
然而Event Loop并沒有上面圖中描述那么簡單。心塞塞 : (
根據規范,事件循環是通過任務隊列的機制來進行協調的。一個 Event Loop 中,可以有一個或者多個任務隊列(task queue),一個任務隊列便是一系列有序任務(task)的集合;每個任務都有一個任務源(task source),源自同一個任務源的 task 必須放到同一個任務隊列,從不同源來的則被添加到不同隊列。
setTimeout/Promise 等API便是任務源,而進入任務隊列的是他們指定的具體執行任務(回調函數)。來自不同任務源的任務會進入到不同的任務隊列。其中setTimeout與setInterval是同源的。
仔細查閱規范可知,異步任務可分為 task(部分文章也稱為 macro-task) 和 micro-task 兩類,不同的API注冊的異步任務會依次進入自身對應的隊列中,然后等待 Event Loop 將它們依次壓入執行棧中執行。
task主要包含:script(整體代碼)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(node.js 環境)
micro-task主要包含:Promise.then、MutaionObserver、MessageChannel、process.nextTick(node.js 環境)
在事件循環中,每進行一次循環操作稱為 tick,每一次 tick 的任務處理模型是比較復雜的,但關鍵步驟如下:
在此次 tick 中選擇最先進入隊列的任務(oldest task),如果有則執行(一次)
檢查是否存在 micro-task,如果存在則不停地執行,直至清空 micro-task queue
更新 render
主線程重復執行上述步驟
一個事件循環(Event Loop)中,主線程從任務隊列中取出一個任務 task 執行時,而這個正在執行的任務就是從 task queue(部分文章也稱為 macro-task queue)中來的。當這個 task 執行結束后,js 會將 micro-task queue中所有 micro-task 都在同一個 Event Loop 中執行,當這些 micro-task 執行結束后還能繼續添加 micro-task 一直到整個 micro-task 隊列執行結束。然后當前本輪的 Event Loop 結束,主線程可以繼續取下一個 task 執行。所以更詳細的 Event Loop 的流程圖如下:
// example console.log("example-start") setTimeout(() => { console.log("setTimeout-0") // setTimeout-1 }, 0) new Promise((resolve, reject) => { console.log("promise-1") resolve("promise-2") Promise.resolve().then(() => console.log("promise-3")) // then-1 }).then((response) => { // then-2 console.log(response) setTimeout(() => { console.log("setTimeout-10") // setTimeout-2 }, 10) }) console.log("example-end") /* chrome result * example-start promise-1 example-end promise-3 promise-2 setTimeout-0 setTimeout-10 * */
上面一個簡單的 js 片段的執行過程:
第一輪事件循環:
第二輪事件循環:
第三輪事件循環:
如果上文理解有誤或者有疑惑,歡迎交流。
參考Philip Roberts: Help, I’m stuck in an event-loop.
JavaScript 運行機制詳解:再談Event Loop
關于JavaScript單線程的一些事
從一道題淺說 JavaScript 的事件循環
Event Loop的規范和實現
這一次,徹底弄懂 JavaScript 執行機制
好記性不如爛筆頭。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95264.html
摘要:深入理解引擎的執行機制靈魂三問為什么是單線程的為什么需要異步單線程又是如何實現異步的呢中的中的說說首先請牢記點是單線程語言的是的執行機制。 深入理解JS引擎的執行機制 1.靈魂三問 : JS為什么是單線程的? 為什么需要異步? 單線程又是如何實現異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說說setTimeout 首先,請牢記2...
摘要:曾經的理解首先,是單線程語言,也就意味著同一個時間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時有兩個線程,一個線程在某個節點上編輯了內容,而另一個線程刪除了這個節點,這時瀏覽器就很懵逼了,到底以執行哪個操作呢所以,設計者把 Event Loop曾經的理解 首先,JS是單線程語言,也就意味著同一個時間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...
摘要:主線程在任務隊列中讀取事件,這個過程是循環不斷地,所以這種運行機制叫做事件循環是在執行棧同步代碼結束之后,下一次任務隊列執行之前。 單線程 javascript為什么是單線程語言,原因在于如果是多線程,當一個線程對DOM節點做添加內容操作的時候,另一個線程要刪除這個DOM節點,這個時候,瀏覽器應該怎么選擇,這就造成了混亂,為了解決這類問題,在一開始的時候,javascript就采用單線...
摘要:深入理解引擎的執行機制最近在反省,很多知識都是只會用,不理解底層的知識。在閱讀之前,請先記住兩點是單線程語言的是的執行機制。所以,是存在異步執行的,比如單線程是怎么實現異步的場景描述通過事件循環,所以說,理解了機制,也就理解了的執行機制啦。 深入理解js引擎的執行機制 最近在反省,很多知識都是只會用,不理解底層的知識。所以在開發過程中遇到一些奇怪的比較難解決的bug,在思考的時候就會收...
摘要:前言前幾天在理解的事件環機制中引發了我對瀏覽器里的好奇。接下來理解瀏覽器中的,先看一張圖堆和棧堆是用戶主動請求而劃分出來的內存區域,比如你,就是將一個對象存入堆中,可以理解為存對象。廢話不多說,直接上圖個人理解。參考資料運行機制詳解再談 前言 前幾天在理解node的事件環機制中引發了我對瀏覽器里Event Loop的好奇。我們都知道javascript是單線程的,任務是需要一個一個按順...
閱讀 2368·2021-11-18 10:07
閱讀 2318·2021-09-22 15:59
閱讀 3077·2021-08-23 09:42
閱讀 2276·2019-08-30 15:44
閱讀 1191·2019-08-29 15:06
閱讀 2303·2019-08-29 13:27
閱讀 1210·2019-08-29 13:21
閱讀 1412·2019-08-29 13:13