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

資訊專欄INFORMATION COLUMN

譯文:JS事件循環機制(event loop)之宏任務、微任務

nemo / 2837人閱讀

摘要:譯文事件循環機制之宏任務微任務原文標題這是一篇谷歌大神文章,寫得非常精彩。為什么會出現這樣打印順序呢要理解這些你首先需要對事件循環機制處理宏任務和微任務的方式有了解。

譯文:JS事件循環機制(event loop)之宏任務、微任務 原文標題:《Tasks, microtasks, queues and schedules》

這是一篇谷歌大神文章,寫得非常精彩。譯者想借這次翻譯深入學習一下,由于水平有限,英文好的同學建議直接閱讀原文。
原文地址: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
但是在不同瀏覽器上的結果卻是讓人懵逼的。

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

為什么會出現這樣打印順序呢?

要理解這些你首先需要對事件循環機制處理宏任務和微任務的方式有了解。
如果是第一次接觸信息量會有點大。深呼吸……

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

宏任務(task)

瀏覽器為了能夠使得JS內部task與DOM任務能夠有序的執行,會在一個task執行結束后,在下一個 task 執行開始前,對頁面進行重新渲染 (task->渲染->task->...)
鼠標點擊會觸發一個事件回調,需要執行一個宏任務,然后解析HTMl。還有下面這個例子,setTimeout

setTimeout的作用是等待給定的時間后為它的回調產生一個新的宏任務。這就是為什么打印‘setTimeout’在‘script end’之后。因為打印‘script end’是第一個宏任務里面的事情,而‘setTimeout’是另一個獨立的任務里面打印的。

微任務(Microtasks )

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

一旦一個pormise有了結果,或者早已有了結果(有了結果是指這個promise到了fulfilled或rejected狀態),他就會為它的回調產生一個微任務,這就保證了回調異步的執行即使這個promise早已有了結果。所以對一個已經有了結果的promise調用.then(yey, nay)會立即產生一個微任務。這就是為什么‘promise1’,"promise2"會打印在‘script end’之后,因為所有微任務執行的時候,當前執行棧的代碼必須已經執行完畢。‘promise1’,"promise2"會打印在‘setTimeout’之前是因為所有微任務總會在下一個宏任務之前全部執行完畢。

逐步執行demo:譯者注,這里作者實現了一個類似于debug,逐步執行的demo,其中還加入了執行棧的動畫還有講解,建議大家去原文觀看
原文

是的,我弄了一個逐步的圖標。你怎么度過你的周六?和你的朋友出去享受陽光?emmmm,如果對我驚艷的ui交互設計看不懂,點擊左右箭頭試試吧。

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

有些瀏覽會會打印出:
script start, script end, setTimeout, promise1, promise2。
他們會在setTimeout之后執行promise的回調,就好像這些瀏覽器會把promise的回調視作一個新的宏任務而不是微任務。

其實無可厚非,因為promises 來自于ECMAScript 的標準而不是HTML標準。
ECMAScript 有個關于jobs的概念和微任務挺類似的,但是否明確具有關聯關系卻尚未定論(相關討論)。然而,普遍的觀點是promise應該屬于微任務。

如果說把 promise 當做一個新的 task 來執行的話,這將會造成一些性能上的問題,因為 promise 的回調函數可能會被延遲執行,因為在每一個 task 執行結束后瀏覽器可能會進行一些渲染工作。由于作為一個 task 將會和其他任務來源(task source)相互影響,這也會造成一些不確定性,同時這也將打破一些與其他 API 的交互,這樣一來便會造成一系列的問題。

這里有一個關于讓Edge把promise加入微任務的提議,其實WebKit 早已悄悄正確實現。所以我猜Safari最終會修復,Firefox 43好像已修復。

如何分辨宏任務和微任務?

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

一級boss戰

寫這篇文章前我就犯了這個錯。來看代碼

在看接下來的js代碼,如果我點擊div.inner會打印什么?

// 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
//監聽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:日志可能出現多次哦。
結果如下:
click
promise
mutate
click
promise
mutate
timeout
timeout
你猜對了嗎。你可能猜對了,但是許多瀏覽器卻不這樣覺得。

譯者注:譯者本機測試
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

哪個是對的?

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

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

如果js執行棧空了,立即執行microtask checkpoint
—— HTML: Cleaning up after a callback
microtask checkpoint 會檢查整個微任務隊列,除非正在執行這個檢查動作。ECMAScript 標準中說到

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環境下,必須執行。

瀏覽器哪里出錯了?

Firefox和Safari在click監聽器回調之間正確執行了mutation 回調的微任務,但promise打印結果卻出現在了錯誤的位置。
無可厚非的是jobs和微任務的關系太含糊不清,不過我仍認為應該在click監聽器回調之間執行。
Edge我們早就知道會把promise回調放進錯誤的隊列,但他也也沒在click監聽器回調之間執行微任務隊列,而是在所有監聽器回調后執行,這打印click之后只打印了一次muteta,為此我給它提了個bug。

一級boss憤怒的大哥來了

用剛才的代碼,如果我們這樣執行會發生什么。

inner.click();

這依舊會開始分發事件,但這次是使用腳本而不是交互點擊。
click
click
promise
mutate
promise
timeout
timeout

我發誓我從chrome得到的答案一直不一樣- -。我已經更新了這個表許許多次了。我覺得我是錯誤地測試了Canary。假如你在 Chrome 中得到了不同的結果,請在評論中告訴我是哪個版本。

為什么不一樣呢?

來看demo發生了什么,原作者的演示demo
所以正確的順序是click, click, promise, mutate, promise, timeout, timeout,看來chrome是對的。
在每個監聽器回調調用之后

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

之前的例子,微任務會在監聽器回調之間執行。但這里的例子,click()會導致事件同步分發,所以在監聽器回調之間Js執行棧不為空,而上述的這個規則保證了微任務不會打斷正在執行的js.這意味著我們不能在監聽器回調之間執行微任務,微任務會在監聽器之后執行。

這能影響到什么?

譯者注:對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.

干得不錯!

總結一下:

宏任務按順序執行,且瀏覽器在每個宏任務之間渲染頁面

所有微任務也按順序執行,且在以下場景會立即執行所有微任務

每個回調之后且js執行棧中為空。

每個宏任務結束后。

希望你已經熟悉了eventloop.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/95119.html

相關文章

  • 深入淺出JavaScript運行機制

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

    mochixuan 評論0 收藏0
  • 深入淺出JavaScript運行機制

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

    魏明 評論0 收藏0
  • 深入淺出JavaScript運行機制

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

    chaosx110 評論0 收藏0
  • JavaScript執行機制事件循環

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

    rose 評論0 收藏0

發表評論

0條評論

nemo

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<