摘要:的宿主最開始本身就是瀏覽器,處理用戶的交互事件。既然是單線程的,那就意味著任務(wù)需要排隊(duì),只有前一個(gè)任務(wù)執(zhí)行完畢,下一個(gè)任務(wù)才能開始,于是就有了任務(wù)隊(duì)列。事件循環(huán)有兩種用于瀏覽上下文的事件循環(huán)和用于的事件循環(huán)。
最近看到Event Loop這個(gè)詞出現(xiàn)的頻率有點(diǎn)高,于是查閱各方資料在此記錄一下。
先不說概念,我們來看段代碼:
console.log("script start"); setTimeout(function() { console.log("setTimeout"); }, 0); Promise.resolve().then(function() { console.log("promise1"); }).then(function() { console.log("promise2"); }); console.log("script end");
復(fù)制這段代碼到控制臺(tái),在Chrome會(huì)輸出如下結(jié)果
Why?
如果想弄清楚原因,就必須得弄清楚今天要提到的概念Event Loop。
運(yùn)行時(shí)概念棧
函數(shù)調(diào)用形成了一個(gè)棧幀。
function foo(b) { var a = 10; return a + b + 11; } function bar(x) { var y = 3; return foo(x * y); } console.log(bar(7)); // 返回 42
當(dāng)調(diào)用 bar 時(shí),創(chuàng)建了第一個(gè)幀 ,幀中包含了 bar 的參數(shù)和局部變量。當(dāng) bar 調(diào)用 foo 時(shí),第二個(gè)幀就被創(chuàng)建,并被壓到第一個(gè)幀之上,幀中包含了 foo 的參數(shù)和局部變量。當(dāng) foo 返回時(shí),最上層的幀就被彈出棧(剩下 bar 函數(shù)的調(diào)用幀 )。當(dāng) bar 返回的時(shí)候,棧就空了。
堆
對(duì)象被分配在一個(gè)堆中,即用以表示一大塊非結(jié)構(gòu)化的內(nèi)存區(qū)域。
隊(duì)列
一個(gè) JavaScript 運(yùn)行時(shí)包含了一個(gè)待處理的消息隊(duì)列。每一個(gè)消息都關(guān)聯(lián)著一個(gè)用以處理這個(gè)消息的函數(shù)。
在事件循環(huán)期間的某個(gè)時(shí)刻,運(yùn)行時(shí)從最先進(jìn)入隊(duì)列的消息開始處理隊(duì)列中的消息。為此,這個(gè)消息會(huì)被移出隊(duì)列,并作為輸入?yún)?shù)調(diào)用與之關(guān)聯(lián)的函數(shù)。正如前面所提到的,調(diào)用一個(gè)函數(shù)總是會(huì)為其創(chuàng)造一個(gè)新的棧幀。
函數(shù)的處理會(huì)一直進(jìn)行到執(zhí)行棧再次為空為止;然后事件循環(huán)將會(huì)處理隊(duì)列中的下一個(gè)消息(如果還有的話)。
為什么JavaScript是單線程稍理解JavaScript的都知道JavaScript是單線程,即同一時(shí)間只能處理一件事情。JavaScript為什么不能是多線程呢,這樣就可以同時(shí)處理多件事情提高效率。
JavaScript的宿主最開始本身就是瀏覽器,處理用戶的交互事件。作為瀏覽器腳本,它只能一次做一件事情,假如用戶點(diǎn)擊一個(gè)按鈕的時(shí)候,需要?jiǎng)h除一個(gè)節(jié)點(diǎn),而另一段代碼此時(shí)又要添加這個(gè)節(jié)點(diǎn),那JavaScript該如何處理,以誰為準(zhǔn)?
所以JavaScript在創(chuàng)造之初就考慮到了這點(diǎn),也決定了它只能是單線程,這是它的核心特征之一。
Event Loop
既然JavaScript是單線程的,那就意味著任務(wù)需要排隊(duì),只有前一個(gè)任務(wù)執(zhí)行完畢,下一個(gè)任務(wù)才能開始,于是就有了任務(wù)隊(duì)列。如果一個(gè)任務(wù)耗時(shí)很長(zhǎng),下面的任務(wù)就得一直等著,明顯不太合理,那么能否先把耗時(shí)很久的任務(wù)先掛起來,先執(zhí)行后面的任務(wù),等IO設(shè)備返回的結(jié)果,再去執(zhí)行之前掛著的任務(wù)。
于是任務(wù)就可以分兩種:同步任務(wù)和異步任務(wù)
同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù),只有"任務(wù)隊(duì)列"通知主線程,某個(gè)異步任務(wù)可以執(zhí)行了,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。
(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開始執(zhí)行。
(4)主線程不斷重復(fù)上面的第三步。
異步任務(wù)指的是異步的代碼加入到任務(wù)隊(duì)列中,等待主線程通知執(zhí)行Event Loop
主線程從"任務(wù)隊(duì)列"中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop!
Event loop:客戶端必須使用本章節(jié)中所描述的事件循環(huán),來協(xié)調(diào)事件,用戶交互,腳本,呈現(xiàn),網(wǎng)絡(luò)等等。 事件循環(huán)有兩種:用于瀏覽上下文的事件循環(huán)和用于 worker 的事件循環(huán)。
任務(wù)隊(duì)列分為宏任務(wù)隊(duì)列(macro tasks) 和 微任務(wù)隊(duì)列(micro tasks)
如何判斷一段代碼是加入到宏任務(wù)隊(duì)列還是微任務(wù)隊(duì)列?
每個(gè)任務(wù)都由特殊任務(wù)源來定義。 來自同一個(gè)特殊任務(wù)源的所有任務(wù)都將發(fā)往特定事件循環(huán)。所以我們可以按照不同的來源進(jìn)行分類,不同來源的任務(wù)都對(duì)應(yīng)到不同的任務(wù)隊(duì)列中
(macro-task 宏任務(wù))來源:I/O, setTimeout + setInterval + setImmediate, UI renderder ···
(micro-task 微任務(wù))來源:Promise ,process.nextTick ,MutationObserver, Object.observe ···
Microtasks are usually scheduled for things that should happen straight after the currently executing script, such as reacting to a batch of actions, or to make something async without taking the penalty of a whole new task. The microtask queue is processed after callbacks as long as no other JavaScript is mid-execution, and at the end of each task. Any additional microtasks queued during microtasks are added to the end of the queue and also processed. Microtasks include mutation observer callbacks, and as in the above example, promise callbacks.
看下完整的執(zhí)行過程:
? 代碼開始執(zhí)行,JavaScript 引擎對(duì)所有的代碼進(jìn)行區(qū)分。
? 同步代碼被壓入棧中,異步代碼根據(jù)不同來源加入到宏任務(wù)隊(duì)列尾部,或者微任務(wù)隊(duì)列的尾部。
? 等待棧中的代碼被執(zhí)行完畢,此時(shí)通知任務(wù)隊(duì)列,執(zhí)行位于隊(duì)列首部的宏任務(wù)。
? 宏任務(wù)執(zhí)行完畢,開始執(zhí)行其關(guān)聯(lián)的微任務(wù)。
? 關(guān)聯(lián)的微任務(wù)執(zhí)行完畢,繼續(xù)執(zhí)行下一個(gè)宏任務(wù),直到任務(wù)隊(duì)列中所有宏任務(wù)被執(zhí)行完畢。
?執(zhí)行下一個(gè)任務(wù)隊(duì)列。
參考文檔:
并發(fā)模型與事件循環(huán)
JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop
什么是瀏覽器的事件循環(huán)(Event Loop)?
從 薛定諤的貓 聊到 Event loop
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/103302.html
摘要:如果當(dāng)前沒有事件也沒有定時(shí)器事件,則返回。相關(guān)資料關(guān)于的架構(gòu)及設(shè)計(jì)思路的事件討論了使用線程池異步運(yùn)行代碼。下一篇初窺事件機(jī)制的實(shí)現(xiàn)二中定時(shí)器的實(shí)現(xiàn) 在瀏覽器中,事件作為一個(gè)極為重要的機(jī)制,給予JavaScript響應(yīng)用戶操作與DOM變化的能力;在Node.js中,事件驅(qū)動(dòng)模型則是其高并發(fā)能力的基礎(chǔ)。 學(xué)習(xí)JavaScript也需要了解它的運(yùn)行平臺(tái),為了更好的理解JavaScript的事...
摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過任務(wù)隊(duì)列的機(jī)制來進(jìn)行協(xié)調(diào)的。等便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當(dāng)前本輪的結(jié)束,主線程可以繼續(xù)取下一個(gè)執(zhí)行。 依然是:經(jīng)濟(jì)基礎(chǔ)決定上層建筑。 說明 首先,旨在搞清常用的同步異步執(zhí)行機(jī)制 其次,暫時(shí)不討論node.js的Event Loop執(zhí)行機(jī)制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機(jī)制 最后,借鑒了很多前輩的研究文...
摘要:了解事件循環(huán)機(jī)制有助于理解的執(zhí)行過程,同時(shí)這也是面試常見題。那么這個(gè)回調(diào)函數(shù)將在何時(shí)由誰執(zhí)行呢已知是瀏覽器環(huán)境提供的,因此瀏覽器將對(duì)它進(jìn)行處理,瀏覽器會(huì)在本次事件完成,即計(jì)時(shí)結(jié)束后,將回調(diào)函數(shù)加入循環(huán)隊(duì)列中,然后等待被加入執(zhí)行棧執(zhí)行。 如果有人問JavaScript是什么,也許你會(huì)說它是一個(gè)單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個(gè)單線程語言,它是怎么實(shí)現(xiàn)非阻塞、異步的?這就...
摘要:眾所周知,是,也就意味著在執(zhí)行的過程中,是,而這樣的特性,正是由一個(gè)叫的東西決定的有且僅有一個(gè)。無論從工程效率還是用戶體驗(yàn)的角度來說,這都是不被允許的一件事情。五秒后,結(jié)束計(jì)時(shí),將回調(diào)函數(shù)下放到中。至此,正式引出的概念。 前段時(shí)間在網(wǎng)上陸續(xù)看了很多關(guān)于 Event loop 的文章,看完也就混個(gè)眼熟,可能內(nèi)心深處對(duì)這種偏原理的知識(shí)有一些抵觸心情,看完后也都沒有去深入理解。最近在看 Vu...
摘要:瀏覽器推遲事件直到所有的腳本都處于狀態(tài)。解析器將處理執(zhí)行這個(gè)腳本。創(chuàng)建這個(gè)腳本的解析器的文檔有正在阻塞腳本執(zhí)行腳本元素為等待解析阻塞的腳本的狀態(tài),同一時(shí)刻只能有一個(gè)這樣的腳本存在。解析器將一個(gè)或多個(gè)字符轉(zhuǎn)換為表并處理,這個(gè)過程是一個(gè)典型的。 前言 本文主要對(duì)W3C規(guī)范中關(guān)于script標(biāo)簽和event loop相關(guān)的篇幅做了簡(jiǎn)單的探討,針對(duì)一些必要的相關(guān)概念進(jìn)行了適當(dāng)?shù)臉?biāo)注和說明。雖然...