成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

什么是瀏覽器的事件循環(huán)(Event Loop)?

JerryC / 870人閱讀

摘要:本文圍繞瀏覽器的事件循環(huán),而有自己的另一套事件循環(huán)機(jī)制,不在本文討論范圍?,F(xiàn)在我們知道了瀏覽器運(yùn)行時(shí)有一個(gè)叫事件循環(huán)的機(jī)制。將事件循環(huán)的當(dāng)前運(yùn)行任務(wù)設(shè)置為。對(duì)于相應(yīng)事件循環(huán)的每個(gè)環(huán)境設(shè)置對(duì)象通知它們哪些為。

本文圍繞瀏覽器的事件循環(huán),而node.js有自己的另一套事件循環(huán)機(jī)制,不在本文討論范圍。網(wǎng)上的許多相關(guān)技術(shù)文章提到了process.nextTicksetImmediate兩個(gè)node.js的API,這里不予討論。

先看HTML標(biāo)準(zhǔn)的一系列解釋?zhuān)?/p>

為了協(xié)調(diào)事件(event),用戶(hù)交互(user interaction),腳本(script),渲染(rendering),網(wǎng)絡(luò)(networking)等,用戶(hù)代理(user agent)必須使用事件循環(huán)(event loops)。

有兩類(lèi)事件循環(huán):一種針對(duì)瀏覽上下文(browsing context),還有一種針對(duì)worker(web worker)。

現(xiàn)在我們知道了瀏覽器運(yùn)行時(shí)有一個(gè)叫事件循環(huán)的機(jī)制。

一個(gè)事件循環(huán)有一個(gè)或者多個(gè)任務(wù)隊(duì)列(task queues)。任務(wù)隊(duì)列是task的有序列表,這些task是以下工作的對(duì)應(yīng)算法:Events,Parsing,Callbacks,Using a resource,Reacting to DOM manipulation。

每一個(gè)任務(wù)都來(lái)自一個(gè)特定的任務(wù)源(task source)。所有來(lái)自一個(gè)特定任務(wù)源并且屬于特定事件循環(huán)的任務(wù),通常必須被加入到同一個(gè)任務(wù)隊(duì)列中,但是來(lái)自不同任務(wù)源的任務(wù)可能會(huì)放在不同的任務(wù)隊(duì)列中。

舉個(gè)例子,用戶(hù)代理有一個(gè)處理鼠標(biāo)和鍵盤(pán)事件的任務(wù)隊(duì)列。用戶(hù)代理可以給這個(gè)隊(duì)列比其他隊(duì)列多3/4的執(zhí)行時(shí)間,以確保交互的響應(yīng)而不讓其他任務(wù)隊(duì)列餓死(starving),并且不會(huì)亂序處理任何一個(gè)任務(wù)隊(duì)列的事件。

每個(gè)事件循環(huán)都有一個(gè)進(jìn)入microtask檢查點(diǎn)(performing a microtask checkpoint)的flag標(biāo)志,這個(gè)標(biāo)志初始為false。它被用來(lái)組織反復(fù)調(diào)用‘進(jìn)入microtask檢查點(diǎn)’的算法。

總結(jié)一下,一個(gè)事件循環(huán)里有很多個(gè)任務(wù)隊(duì)列(task queues)來(lái)自不同任務(wù)源,每一個(gè)任務(wù)隊(duì)列里的任務(wù)是嚴(yán)格按照先進(jìn)先出的順序執(zhí)行的,但是不同任務(wù)隊(duì)列的任務(wù)的執(zhí)行順序是不確定的。按我的理解就是,瀏覽器會(huì)自己調(diào)度不同任務(wù)隊(duì)列。網(wǎng)上很多文章會(huì)提到macrotask這個(gè)概念,其實(shí)就是指代了標(biāo)準(zhǔn)里闡述的task。

標(biāo)準(zhǔn)同時(shí)還提到了microtask的概念,也就是微任務(wù)??匆幌聵?biāo)準(zhǔn)闡述的事件循環(huán)的進(jìn)程模型:

選擇當(dāng)前要執(zhí)行的任務(wù)隊(duì)列,選擇一個(gè)最先進(jìn)入任務(wù)隊(duì)列的任務(wù),如果沒(méi)有任務(wù)可以選擇,則會(huì)跳轉(zhuǎn)至microtask的執(zhí)行步驟。

將事件循環(huán)的當(dāng)前運(yùn)行任務(wù)設(shè)置為已選擇的任務(wù)。

運(yùn)行任務(wù)。

將事件循環(huán)的當(dāng)前運(yùn)行任務(wù)設(shè)置為null。

將運(yùn)行完的任務(wù)從任務(wù)隊(duì)列中移除。

microtasks步驟:進(jìn)入microtask檢查點(diǎn)(performing a microtask checkpoint )。

更新界面渲染。

返回第一步。

執(zhí)行進(jìn)入microtask檢查點(diǎn)時(shí),用戶(hù)代理會(huì)執(zhí)行以下步驟:

設(shè)置進(jìn)入microtask檢查點(diǎn)的標(biāo)志為true。

當(dāng)事件循環(huán)的微任務(wù)隊(duì)列不為空時(shí):選擇一個(gè)最先進(jìn)入microtask隊(duì)列的microtask;設(shè)置事件循環(huán)的當(dāng)前運(yùn)行任務(wù)為已選擇的microtask;運(yùn)行microtask;設(shè)置事件循環(huán)的當(dāng)前運(yùn)行任務(wù)為null;將運(yùn)行結(jié)束的microtask從microtask隊(duì)列中移除。

對(duì)于相應(yīng)事件循環(huán)的每個(gè)環(huán)境設(shè)置對(duì)象(environment settings object),通知它們哪些promise為rejected。

清理indexedDB的事務(wù)。

設(shè)置進(jìn)入microtask檢查點(diǎn)的標(biāo)志為false。

現(xiàn)在我們知道了。在事件循環(huán)中,用戶(hù)代理會(huì)不斷從task隊(duì)列中按順序取task執(zhí)行,每執(zhí)行完一個(gè)task都會(huì)檢查microtask隊(duì)列是否為空(執(zhí)行完一個(gè)task的具體標(biāo)志是函數(shù)執(zhí)行棧為空),如果不為空則會(huì)一次性執(zhí)行完所有microtask。然后再進(jìn)入下一個(gè)循環(huán)去task隊(duì)列中取下一個(gè)task執(zhí)行...

那么哪些行為屬于task或者microtask呢?標(biāo)準(zhǔn)沒(méi)有闡述,但各種技術(shù)文章總結(jié)都如下:

macrotasks: script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering

microtasks: process.nextTick, Promises, Object.observe(廢棄), MutationObserver

來(lái)看一個(gè)例子:

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");

(代碼來(lái)自Tasks, microtasks, queues and schedules,推薦觀(guān)看原文的代碼可視化執(zhí)行步驟

如果你測(cè)試的瀏覽器支持的Promise不支持Promise/A+標(biāo)準(zhǔn),或是你使用了其他Promise polyfill,運(yùn)行結(jié)果可能有差異。

運(yùn)行結(jié)果是:

script start
script end
promise1
promise2
setTimeout

解釋一下過(guò)程。

一開(kāi)始task隊(duì)列中只有script,則script中所有函數(shù)放入函數(shù)執(zhí)行棧執(zhí)行,代碼按順序執(zhí)行。

接著遇到了setTimeout,它的作用是0ms后將回調(diào)函數(shù)放入task隊(duì)列中,也就是說(shuō)這個(gè)函數(shù)將在下一個(gè)事件循環(huán)中執(zhí)行(注意這時(shí)候setTimeout執(zhí)行完畢就返回了)。

接著遇到了Promise,按照前面所述Promise屬于microtask,所以第一個(gè).then()會(huì)放入microtask隊(duì)列。

當(dāng)所有script代碼執(zhí)行完畢后,此時(shí)函數(shù)執(zhí)行棧為空。開(kāi)始檢查microtask隊(duì)列,此時(shí)隊(duì)列不為空,執(zhí)行.then()的回調(diào)函數(shù)輸出"promise1",由于.then()返回的依然是promise,所以第二個(gè).then()會(huì)放入microtask隊(duì)列繼續(xù)執(zhí)行,輸出"promise2"。

此時(shí)microtask隊(duì)列為空了,進(jìn)入下一個(gè)事件循環(huán),檢查task隊(duì)列發(fā)現(xiàn)了setTimeout的回調(diào)函數(shù),立即執(zhí)行回調(diào)函數(shù)輸出"setTimeout",代碼執(zhí)行完畢。

繼續(xù)看一個(gè)更有趣的例子:

HTML代碼:

JavaScript代碼:

// Let"s get hold of those elements
var outer = document.querySelector(".outer");
var inner = document.querySelector(".inner");

// Let"s listen for attribute changes on the
// outer element
new MutationObserver(function() {
  console.log("mutate");
}).observe(outer, {
  attributes: true
});

// Here"s a click listener…
function onClick() {
  console.log("click");

  setTimeout(function() {
    console.log("timeout");
  }, 0);

  Promise.resolve().then(function() {
    console.log("promise");
  });

  outer.setAttribute("data-random", Math.random());
}

// …which we"ll attach to both elements
inner.addEventListener("click", onClick);
outer.addEventListener("click", onClick);

(代碼來(lái)自Tasks, microtasks, queues and schedules,推薦觀(guān)看原文的代碼可視化執(zhí)行步驟
點(diǎn)擊內(nèi)框后,結(jié)果如下:

click
promise
mutate
click
promise
mutate
timeout
timeout

解釋一下過(guò)程:
點(diǎn)擊inner輸出"click",Promise和設(shè)置outer屬性會(huì)依次把Promise和MutationObserver推入microtask隊(duì)列,setTimeout則會(huì)推入task隊(duì)列。此時(shí)執(zhí)行棧為空,雖然后面還有冒泡觸發(fā),但是此時(shí)microtask隊(duì)列會(huì)先執(zhí)行,所以依次輸入"promise"和"mutate"。接下來(lái)事件冒泡再次觸發(fā)事件,過(guò)程和開(kāi)始一樣。接著代碼執(zhí)行完畢,此時(shí)進(jìn)入下一次事件循環(huán),執(zhí)行task隊(duì)列中的任務(wù),輸出兩個(gè)"timeout"。

好了,如果你理解了這個(gè),那么現(xiàn)在換一下事件觸發(fā)的方式。在上面的代碼后面加上

inner.click()

思考看看會(huì)有什么不同。

運(yùn)行結(jié)果:

click
click
promise
mutate
promise
timeout
timeout

造成這個(gè)差異的結(jié)果是什么呢?因?yàn)榈谝淮螆?zhí)行完第一個(gè)click事件后函數(shù)執(zhí)行棧并不為空。
具體代碼運(yùn)行解釋?zhuān)梢圆榭碩asks, microtasks, queues and schedules。

本文參考:
html.spec.whatwg.org
difference-between-javascript-macrotask-and-microtask
Event loop

墻裂建議大家閱讀HTML標(biāo)準(zhǔn)里闡述的Event Loop,歡迎指正和建議。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87228.html

相關(guān)文章

  • 一篇文章教會(huì)你Event loop——覽器和Node

    摘要:如果沒(méi)到毫秒,那么階段就會(huì)跳過(guò),進(jìn)入階段,先執(zhí)行的回調(diào)函數(shù)。參考文檔什么是瀏覽器的事件循環(huán)不要混淆和瀏覽器中的定時(shí)器詳解瀏覽器和不同的事件循環(huán)深入理解事件循環(huán)機(jī)制篇中的執(zhí)行機(jī)制 最近對(duì)Event loop比較感興趣,所以了解了一下。但是發(fā)現(xiàn)整個(gè)Event loop盡管有很多篇文章,但是沒(méi)有一篇可以看完就對(duì)它所有內(nèi)容都了解的文章。大部分的文章都只闡述了瀏覽器或者Node二者之一,沒(méi)有對(duì)比...

    Leck1e 評(píng)論0 收藏0
  • 徹底搞懂覽器Event-loop

    摘要:檢查宏任務(wù)隊(duì)列,發(fā)現(xiàn)有的回調(diào)函數(shù)立即執(zhí)行回調(diào)函數(shù)輸出。接著遇到它的作用是在后將回調(diào)函數(shù)放到宏任務(wù)隊(duì)列中這個(gè)任務(wù)在再下一次的事件循環(huán)中執(zhí)行。 為什么會(huì)寫(xiě)這篇博文呢? 前段時(shí)間,和頭條的小伙伴聊天問(wèn)頭條面試前端會(huì)問(wèn)哪些問(wèn)題,他稱(chēng)如果是他面試的話(huà),event-loop肯定是要問(wèn)的。那天聊了蠻多,event-loop算是給我留下了很深的印象,原因很簡(jiǎn)單,因?yàn)橹拔覐奈瓷钊肓私膺^(guò),如果是面試的時(shí)...

    source 評(píng)論0 收藏0
  • JavaScript 事件循環(huán)(譯文JavaScript Event Loop

    摘要:事件循環(huán)了解了在引擎中是如何工作了之后,來(lái)看下如何使用異步回調(diào)函數(shù)來(lái)避免代碼。從回調(diào)函數(shù)被放入后秒鐘,把移到中。由于事件循環(huán)持續(xù)地監(jiān)測(cè)調(diào)用棧是否已空,此時(shí)它一注意到調(diào)用??樟耍驼{(diào)用并創(chuàng)建一個(gè)新的調(diào)用棧。 聽(tīng)多了JavaScript單線(xiàn)程,異步,V8,便會(huì)很想去知道JavaScript是如何利用單線(xiàn)程來(lái)實(shí)現(xiàn)所謂的異步的。我參考了一些文章,了解到一個(gè)很重要的詞匯:事件循環(huán)(Event L...

    K_B_Z 評(píng)論0 收藏0
  • 關(guān)于覽器Event Loop

    摘要:的宿主最開(kāi)始本身就是瀏覽器,處理用戶(hù)的交互事件。既然是單線(xiàn)程的,那就意味著任務(wù)需要排隊(duì),只有前一個(gè)任務(wù)執(zhí)行完畢,下一個(gè)任務(wù)才能開(kāi)始,于是就有了任務(wù)隊(duì)列。事件循環(huán)有兩種用于瀏覽上下文的事件循環(huán)和用于的事件循環(huán)。 最近看到Event Loop這個(gè)詞出現(xiàn)的頻率有點(diǎn)高,于是查閱各方資料在此記錄一下。 先不說(shuō)概念,我們來(lái)看段代碼: console.log(script start); setT...

    youkede 評(píng)論0 收藏0
  • JavaScript執(zhí)行機(jī)制、事件循環(huán)

    摘要:曾經(jīng)的理解首先,是單線(xiàn)程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么為什么不是多線(xiàn)程呢這樣還能提高效率啊假定同時(shí)有兩個(gè)線(xiàn)程,一個(gè)線(xiàn)程在某個(gè)節(jié)點(diǎn)上編輯了內(nèi)容,而另一個(gè)線(xiàn)程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器就很懵逼了,到底以執(zhí)行哪個(gè)操作呢所以,設(shè)計(jì)者把 Event Loop曾經(jīng)的理解 首先,JS是單線(xiàn)程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么 為什么JavaScript不是多線(xiàn)程呢?這樣還能提...

    rose 評(píng)論0 收藏0
  • 10分鐘理解JS引擎執(zhí)行機(jī)制

    摘要:深入理解引擎的執(zhí)行機(jī)制靈魂三問(wèn)為什么是單線(xiàn)程的為什么需要異步單線(xiàn)程又是如何實(shí)現(xiàn)異步的呢中的中的說(shuō)說(shuō)首先請(qǐng)牢記點(diǎn)是單線(xiàn)程語(yǔ)言的是的執(zhí)行機(jī)制。 深入理解JS引擎的執(zhí)行機(jī)制 1.靈魂三問(wèn) : JS為什么是單線(xiàn)程的? 為什么需要異步? 單線(xiàn)程又是如何實(shí)現(xiàn)異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說(shuō)說(shuō)setTimeout 首先,請(qǐng)牢記2...

    zzbo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<