摘要:引擎是單線程的,如上圖中,它負責維護任務隊列,并通過的機制,按順序把任務放入棧中執行。接下來,我們會細說圖中的棧和任務隊列。直到微任務隊列為空,執行下一步。上一輪循環中有少數的會被延遲到這一輪的這一階段執行。
概覽
我們經常會聽到引擎和runtime,它們的區別是什么呢?
引擎:解釋并編譯代碼,讓它變成能交給機器運行的代碼(runnable commands)。
runtime:就是運行環境,它提供一些對外接口供Js調用,以跟外界打交道,比如,瀏覽器環境、Node.js環境。不同的runtime,會提供不同的接口,比如,在 Node.js 環境中,我們可以通過 require 來引入模塊;而在瀏覽器中,我們有 window、 DOM。
Js引擎是單線程的,如上圖中,它負責維護任務隊列,并通過 Event Loop 的機制,按順序把任務放入棧中執行。而圖中的異步處理模塊,就是 runtime 提供的,擁有和Js引擎互不干擾的線程。接下來,我們會細說圖中的:棧和任務隊列。
棧現在,我們要運行下面這段代碼:
function bar() { console.log(1); } function foo() { console.log(2); far(); } setTimeout(() => { console.log(3) }); foo();
它在棧中的入棧、出棧過程,如下圖:
Js 中,有兩類任務隊列:宏任務隊列(macro tasks)和微任務隊列(micro tasks)。宏任務隊列可以有多個,微任務隊列只有一個。那么什么任務,會分到哪個隊列呢?
宏任務:script(全局任務), setTimeout, setInterval, setImmediate, I/O, UI rendering.
微任務:process.nextTick, Promise, Object.observer, MutationObserver.
瀏覽器的 Event Loop瀏覽器的 Event Loop 遵循的是 HTML5 標準,而 NodeJs 的 Event Loop 遵循的是 libuv。 區別較大,分開講。
我們上面講到,當stack空的時候,就會從任務隊列中,取任務來執行。瀏覽器這邊,共分3步:
取一個宏任務來執行。執行完畢后,下一步。
取一個微任務來執行,執行完畢后,再取一個微任務來執行。直到微任務隊列為空,執行下一步。
更新UI渲染。
Event Loop 會無限循環執行上面3步,這就是Event Loop的主要控制邏輯。其中,第3步(更新UI渲染)會根據瀏覽器的邏輯,決定要不要馬上執行更新。畢竟更新UI成本大,所以,一般都會比較長的時間間隔,執行一次更新。
從執行步驟來看,我們發現微任務,受到了特殊待遇!我們代碼開始執行都是從script(全局任務)開始,所以,一旦我們的全局任務(屬于宏任務)執行完,就馬上執行完整個微任務隊列。看個例子:
console.log("script start"); // 微任務 Promise.resolve().then(() => { console.log("p 1"); }); // 宏任務 setTimeout(() => { console.log("setTimeout"); }, 0); var s = new Date(); while(new Date() - s < 50); // 阻塞50ms // 微任務 Promise.resolve().then(() => { console.log("p 2"); }); console.log("script ent"); /*** output ***/ // one macro task script start script ent // all micro tasks p 1 p 2 // one macro task again setTimeout
上面之所以加50ms的阻塞,是因為 setTimeout 的 delayTime 最少是 4ms. 為了避免認為 setTimeout 是因為4ms的延遲而后面才被執行的,我們加了50ms阻塞。
NodeJs 的 Event LoopNodeJs 的運行是這樣的:
初始化 Event Loop
執行您的主代碼。這里同樣,遇到異步處理,就會分配給對應的隊列。直到主代碼執行完畢。
執行主代碼中出現的所有微任務:先執行完所有nextTick(),然后在執行其它所有微任務。
開始 Event Loop
NodeJs 的 Event Loop 分6個階段執行:
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
以上的6個階段,具體處理的任務如下:
timers: 這個階段執行setTimeout()和setInterval()設定的回調。
pending callbacks: 上一輪循環中有少數的 I/O callback 會被延遲到這一輪的這一階段執行。
idle, prepare: 僅內部使用。
poll: 執行 I/O callback,在適當的條件下會阻塞在這個階段
check: 執行setImmediate()設定的回調。
close callbacks: 執行比如socket.on("close", ...)的回調。
每個階段執行完畢后,都會執行所有微任務(先 nextTick,后其它),然后再進入下一個階段。
LinksEvent loops
NodeJs 的 Event Loop 官方文檔
并發模型與事件循環
Philip Roberts: Help, I’m stuck in an event-loop.
Promise的隊列與setTimeout的隊列有何關聯?
JavaScript:徹底理解同步、異步和事件循環(Event Loop)
從event loop規范探究javaScript異步及瀏覽器更新渲染時機
JavaScript 運行機制詳解:再談Event Loop - 阮一峰的網絡日志
Tasks, microtasks, queues and schedules
WindowOrWorkerGlobalScope.setTimeout()
What is the difference between JavaScript Engine and JavaScript Runtime Environment
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91862.html
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執行機制和事件循環 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:單線程異步非阻塞然后,這又牽扯到了事件循環消息隊列,還有微任務宏任務這些。此步的位置不確定某個時刻后,定時器觸發線程通知事件觸發線程,事件觸發線程將回調函數加入消息隊列隊尾,等待引擎線程執行。 前言 Philip Roberts 在演講 great talk at JSConf on the event loop 中說:要是用一句話來形容 JavaScript,我可能會這樣: Java...
摘要:當函數結束,將會被從調用棧移出。事件循環事件循環的責任就是查看調用棧并確定調用棧是否為空。事件循環會再次檢查調用棧是否為空,如果為空的話,它會把事件回調壓入棧中,然后回調函數則被執行。 寫在文章前 這篇文章是翻譯自Sukhjinder Arora的Understanding Asynchronous JavaScript。這篇文章描述了異步和同步JavaScript是如何在運行環境中,...
摘要:只要指定過回調函數,這些事件發生時就會進入任務隊列,等待主線程讀取。三主線程從任務隊列中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為事件循環。 一、任務隊列 同步任務與異步任務的由來 單線程就意味著,所有任務需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等著。 如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候C...
摘要:事件循環當主線程中的任務執行完畢后,會從任務隊列中獲取任務一個個的放在棧中執行去執行,這個過程是循環不斷的,所以整個的這種運行機制又稱為事件循環。 寫在前面 說起javascript(以下簡稱js)這門語言,相信大家已經非常熟悉了,不管是前端開發還是后端開發幾乎無時無刻都要跟它打交道。雖說開發者每天幾乎都要操作js,但是你真的確定你掌握了js的運行機制嗎!下面我們就來聊聊這話題。 Ja...
摘要:異步任務必須指定回調函數,當異步任務從任務隊列回到執行棧,回調函數就會執行。事件循環主線程從任務隊列中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為。事件循環事件循環是指主線程重復從消息隊列中取消息執行的過程。 參考鏈接:這一次,徹底弄懂 JavaScript 執行機制https://zhuanlan.zhihu.com/p/...從瀏覽器多進程到JS單線程,JS運行機制...
閱讀 2306·2021-11-23 10:09
閱讀 2885·2021-10-12 10:11
閱讀 2594·2021-09-29 09:35
閱讀 1337·2019-08-30 15:53
閱讀 2261·2019-08-30 11:15
閱讀 2904·2019-08-29 13:01
閱讀 2290·2019-08-28 18:15
閱讀 3363·2019-08-26 12:13