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