国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

譯文:JS事件循環(huán)機(jī)制(event loop)之宏任務(wù)、微任務(wù)

nemo / 2836人閱讀

摘要:譯文事件循環(huán)機(jī)制之宏任務(wù)微任務(wù)原文標(biāo)題這是一篇谷歌大神文章,寫得非常精彩。為什么會(huì)出現(xiàn)這樣打印順序呢要理解這些你首先需要對(duì)事件循環(huán)機(jī)制處理宏任務(wù)和微任務(wù)的方式有了解。

譯文:JS事件循環(huán)機(jī)制(event loop)之宏任務(wù)、微任務(wù) 原文標(biāo)題:《Tasks, microtasks, queues and schedules》

這是一篇谷歌大神文章,寫得非常精彩。譯者想借這次翻譯深入學(xué)習(xí)一下,由于水平有限,英文好的同學(xué)建議直接閱讀原文。
原文地址:Tasks, microtasks, queues and schedules
下面正文開始:

Tasks, microtasks, queues and schedules

首先看一段代碼:

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

打印順序是什么?
正確答案是
script start, script end, promise1, promise2, setTimeout
但是在不同瀏覽器上的結(jié)果卻是讓人懵逼的。

Microsoft Edge, Firefox 40, iOS Safari 和 desktop Safari 8.0.8在promise1,promise2之前打印了setTimeout,--雖然看起來像競(jìng)態(tài)條件。
但讓人懵逼的是Firefox 39 , Safari 8.0.7會(huì)打印出正確順序。
譯者注:譯者的Microsoft Edge 38.14393.2068.0,F(xiàn)irefox 59.0.2 版本會(huì)打印出正確順序,應(yīng)該已經(jīng)支持了吧,其他瀏覽器未驗(yàn)證。

為什么會(huì)出現(xiàn)這樣打印順序呢?

要理解這些你首先需要對(duì)事件循環(huán)機(jī)制處理宏任務(wù)和微任務(wù)的方式有了解。
如果是第一次接觸信息量會(huì)有點(diǎn)大。深呼吸……

每個(gè)線程都會(huì)有它自己的event loop(事件循環(huán)),所以都能獨(dú)立運(yùn)行。然而所有同源窗口會(huì)共享一個(gè)event loop以同步通信。event loop會(huì)一直運(yùn)行,來執(zhí)行進(jìn)入隊(duì)列的宏任務(wù)。一個(gè)event loop有多種的宏任務(wù)源(譯者注:event等等),這些宏任務(wù)源保證了在本任務(wù)源內(nèi)的順序。但是瀏覽器每次都會(huì)選擇一個(gè)源中的一個(gè)宏任務(wù)去執(zhí)行。這保證了瀏覽器給與一些宏任務(wù)(如用戶輸入)以更高的優(yōu)先級(jí)。好的,跟著我繼續(xù)……

宏任務(wù)(task)

瀏覽器為了能夠使得JS內(nèi)部task與DOM任務(wù)能夠有序的執(zhí)行,會(huì)在一個(gè)task執(zhí)行結(jié)束后,在下一個(gè) task 執(zhí)行開始前,對(duì)頁(yè)面進(jìn)行重新渲染 (task->渲染->task->...)
鼠標(biāo)點(diǎn)擊會(huì)觸發(fā)一個(gè)事件回調(diào),需要執(zhí)行一個(gè)宏任務(wù),然后解析HTMl。還有下面這個(gè)例子,setTimeout

setTimeout的作用是等待給定的時(shí)間后為它的回調(diào)產(chǎn)生一個(gè)新的宏任務(wù)。這就是為什么打印‘setTimeout’在‘script end’之后。因?yàn)榇蛴 畇cript end’是第一個(gè)宏任務(wù)里面的事情,而‘setTimeout’是另一個(gè)獨(dú)立的任務(wù)里面打印的。

微任務(wù)(Microtasks )

微任務(wù)通常來說就是需要在當(dāng)前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù),比如對(duì)一系列動(dòng)作做出反饋,或或者是需要異步的執(zhí)行任務(wù)而又不需要分配一個(gè)新的 task,這樣便可以減小一點(diǎn)性能的開銷。只要執(zhí)行棧中沒有其他的js代碼正在執(zhí)行且每個(gè)宏任務(wù)執(zhí)行完,微任務(wù)隊(duì)列會(huì)立即執(zhí)行。如果在微任務(wù)執(zhí)行期間微任務(wù)隊(duì)列加入了新的微任務(wù),會(huì)將新的微任務(wù)加入隊(duì)列尾部,之后也會(huì)被執(zhí)行。微任務(wù)包括了mutation observe的回調(diào)還有接下來的例子promise的回調(diào)。

一旦一個(gè)pormise有了結(jié)果,或者早已有了結(jié)果(有了結(jié)果是指這個(gè)promise到了fulfilled或rejected狀態(tài)),他就會(huì)為它的回調(diào)產(chǎn)生一個(gè)微任務(wù),這就保證了回調(diào)異步的執(zhí)行即使這個(gè)promise早已有了結(jié)果。所以對(duì)一個(gè)已經(jīng)有了結(jié)果的promise調(diào)用.then(yey, nay)會(huì)立即產(chǎn)生一個(gè)微任務(wù)。這就是為什么‘promise1’,"promise2"會(huì)打印在‘script end’之后,因?yàn)樗形⑷蝿?wù)執(zhí)行的時(shí)候,當(dāng)前執(zhí)行棧的代碼必須已經(jīng)執(zhí)行完畢。‘promise1’,"promise2"會(huì)打印在‘setTimeout’之前是因?yàn)樗形⑷蝿?wù)總會(huì)在下一個(gè)宏任務(wù)之前全部執(zhí)行完畢。

逐步執(zhí)行demo:譯者注,這里作者實(shí)現(xiàn)了一個(gè)類似于debug,逐步執(zhí)行的demo,其中還加入了執(zhí)行棧的動(dòng)畫還有講解,建議大家去原文觀看
原文

是的,我弄了一個(gè)逐步的圖標(biāo)。你怎么度過你的周六?和你的朋友出去享受陽(yáng)光?emmmm,如果對(duì)我驚艷的ui交互設(shè)計(jì)看不懂,點(diǎn)擊左右箭頭試試吧。

那為什么那些瀏覽器打印順序不一樣咧?

有些瀏覽會(huì)會(huì)打印出:
script start, script end, setTimeout, promise1, promise2。
他們會(huì)在setTimeout之后執(zhí)行promise的回調(diào),就好像這些瀏覽器會(huì)把promise的回調(diào)視作一個(gè)新的宏任務(wù)而不是微任務(wù)。

其實(shí)無可厚非,因?yàn)閜romises 來自于ECMAScript 的標(biāo)準(zhǔn)而不是HTML標(biāo)準(zhǔn)。
ECMAScript 有個(gè)關(guān)于jobs的概念和微任務(wù)挺類似的,但是否明確具有關(guān)聯(lián)關(guān)系卻尚未定論(相關(guān)討論)。然而,普遍的觀點(diǎn)是promise應(yīng)該屬于微任務(wù)。

如果說把 promise 當(dāng)做一個(gè)新的 task 來執(zhí)行的話,這將會(huì)造成一些性能上的問題,因?yàn)?promise 的回調(diào)函數(shù)可能會(huì)被延遲執(zhí)行,因?yàn)樵诿恳粋€(gè) task 執(zhí)行結(jié)束后瀏覽器可能會(huì)進(jìn)行一些渲染工作。由于作為一個(gè) task 將會(huì)和其他任務(wù)來源(task source)相互影響,這也會(huì)造成一些不確定性,同時(shí)這也將打破一些與其他 API 的交互,這樣一來便會(huì)造成一系列的問題。

這里有一個(gè)關(guān)于讓Edge把promise加入微任務(wù)的提議,其實(shí)WebKit 早已悄悄正確實(shí)現(xiàn)。所以我猜Safari最終會(huì)修復(fù),F(xiàn)irefox 43好像已修復(fù)。

如何分辨宏任務(wù)和微任務(wù)?

實(shí)際測(cè)試是一種方法,觀察日志打印順序與promise和setTimeout的關(guān)系,但是首先瀏覽器對(duì)這兩者的實(shí)現(xiàn)要正確。
還有一個(gè)穩(wěn)妥方法就是看文檔,比如setTimeout是宏任務(wù),mutation是微任務(wù)。
正如上文提到的,ECMAScript 中把微任務(wù)叫做jobs,EnqueueJob
是微任務(wù)。
接下來,讓我們看一些復(fù)雜的例子吧

一級(jí)boss戰(zhàn)

寫這篇文章前我就犯了這個(gè)錯(cuò)。來看代碼

在看接下來的js代碼,如果我點(diǎn)擊div.inner會(huì)打印什么?

// 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
//監(jiān)聽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);

偷看答案前先試一試啊,tips:日志可能出現(xiàn)多次哦。
結(jié)果如下:
click
promise
mutate
click
promise
mutate
timeout
timeout
你猜對(duì)了嗎。你可能猜對(duì)了,但是許多瀏覽器卻不這樣覺得。

譯者注:譯者本機(jī)測(cè)試
Chrome( 64.0.3282.167(正式版本) (64 位))相同,
Edge(Edge 38.14393.2068.0)不同(與Chrome順序相同)
Firefox 32位 59.0.2

click

mutate

click

mutate

promise

promise

timeout

timeout

哪個(gè)是對(duì)的?

分發(fā)click event是一個(gè)宏任務(wù),Mutation observer和promise都會(huì)進(jìn)入微任務(wù)隊(duì)列,setTimeout回調(diào)是一個(gè)宏任務(wù),所以來看demo
作者演示demo,建議原文觀看demo
所以chrome是對(duì)的,我之前也不知道只要執(zhí)行棧中沒有js代碼在執(zhí)行,微任務(wù)會(huì)在回調(diào)后立即執(zhí)行,我之前認(rèn)為它只會(huì)在宏任務(wù)結(jié)束后執(zhí)行(Although we are mid-task,microtasks are processed after callbacks if the stack is empty).這個(gè)規(guī)則來自于HTML標(biāo)準(zhǔn)中關(guān)于回調(diào)調(diào)用的部分

If the stack of script settings objects is now empty, perform a microtask checkpoint
— HTML: Cleaning up after a callback step 3

如果js執(zhí)行棧空了,立即執(zhí)行microtask checkpoint
—— HTML: Cleaning up after a callback
microtask checkpoint 會(huì)檢查整個(gè)微任務(wù)隊(duì)列,除非正在執(zhí)行這個(gè)檢查動(dòng)作。ECMAScript 標(biāo)準(zhǔn)中說到

Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty…
— ECMAScript: Jobs and Job Queues

HTML環(huán)境下,必須執(zhí)行。

瀏覽器哪里出錯(cuò)了?

Firefox和Safari在click監(jiān)聽器回調(diào)之間正確執(zhí)行了mutation 回調(diào)的微任務(wù),但promise打印結(jié)果卻出現(xiàn)在了錯(cuò)誤的位置。
無可厚非的是jobs和微任務(wù)的關(guān)系太含糊不清,不過我仍認(rèn)為應(yīng)該在click監(jiān)聽器回調(diào)之間執(zhí)行。
Edge我們?cè)缇椭罆?huì)把promise回調(diào)放進(jìn)錯(cuò)誤的隊(duì)列,但他也也沒在click監(jiān)聽器回調(diào)之間執(zhí)行微任務(wù)隊(duì)列,而是在所有監(jiān)聽器回調(diào)后執(zhí)行,這打印click之后只打印了一次muteta,為此我給它提了個(gè)bug。

一級(jí)boss憤怒的大哥來了

用剛才的代碼,如果我們這樣執(zhí)行會(huì)發(fā)生什么。

inner.click();

這依舊會(huì)開始分發(fā)事件,但這次是使用腳本而不是交互點(diǎn)擊。
click
click
promise
mutate
promise
timeout
timeout

我發(fā)誓我從chrome得到的答案一直不一樣- -。我已經(jīng)更新了這個(gè)表許許多次了。我覺得我是錯(cuò)誤地測(cè)試了Canary。假如你在 Chrome 中得到了不同的結(jié)果,請(qǐng)?jiān)谠u(píng)論中告訴我是哪個(gè)版本。

為什么不一樣呢?

來看demo發(fā)生了什么,原作者的演示demo
所以正確的順序是click, click, promise, mutate, promise, timeout, timeout,看來chrome是對(duì)的。
在每個(gè)監(jiān)聽器回調(diào)調(diào)用之后

If the stack of script settings objects is now empty, perform a microtask checkpoint
— HTML: Cleaning up after a callback step 3

之前的例子,微任務(wù)會(huì)在監(jiān)聽器回調(diào)之間執(zhí)行。但這里的例子,click()會(huì)導(dǎo)致事件同步分發(fā),所以在監(jiān)聽器回調(diào)之間Js執(zhí)行棧不為空,而上述的這個(gè)規(guī)則保證了微任務(wù)不會(huì)打斷正在執(zhí)行的js.這意味著我們不能在監(jiān)聽器回調(diào)之間執(zhí)行微任務(wù),微任務(wù)會(huì)在監(jiān)聽器之后執(zhí)行。

這能影響到什么?

譯者注:對(duì)IndexedDB 理解不深入,這段就不翻譯了- -
Yeah, it"ll bite you in obscure places (ouch). I encountered this while trying to create a simple wrapper library for IndexedDB that uses promises rather than weird IDBRequest objects. It almost makes IDB fun to use.

When IDB fires a success event, the related transaction object becomes inactive after dispatching (step 4). If I create a promise that resolves when this event fires, the callbacks should run before step 4 while the transaction is still active, but that doesn"t happen in browsers other than Chrome, rendering the library kinda useless.

You can actually work around this problem in Firefox, because promise polyfills such as es6-promise use mutation observers for callbacks, which correctly use microtasks. Safari seems to suffer from race conditions with that fix, but that could just be their broken implementation of IDB. Unfortunately, things consistently fail in IE/Edge, as mutation events aren"t handled after callbacks.

Hopefully we"ll start to see some interoperability here soon.

干得不錯(cuò)!

總結(jié)一下:

宏任務(wù)按順序執(zhí)行,且瀏覽器在每個(gè)宏任務(wù)之間渲染頁(yè)面

所有微任務(wù)也按順序執(zhí)行,且在以下場(chǎng)景會(huì)立即執(zhí)行所有微任務(wù)

每個(gè)回調(diào)之后且js執(zhí)行棧中為空。

每個(gè)宏任務(wù)結(jié)束后。

希望你已經(jīng)熟悉了eventloop.

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/95119.html

相關(guān)文章

  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    mochixuan 評(píng)論0 收藏0
  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    魏明 評(píng)論0 收藏0
  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

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

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

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

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

0條評(píng)論

nemo

|高級(jí)講師

TA的文章

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