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

資訊專欄INFORMATION COLUMN

詳解JavaScript的任務(wù)、微任務(wù)、隊(duì)列以及代碼執(zhí)行順序

rubyshen / 1544人閱讀

摘要:在微任務(wù)期間排隊(duì)的任何其他微任務(wù)都會(huì)被添加到隊(duì)列的末尾并進(jìn)行處理。因此一個(gè)已的調(diào)用時(shí)將立即把一個(gè)微任務(wù)加入微任務(wù)隊(duì)列中。和回調(diào)被列為微任務(wù)。上述規(guī)則確保微任務(wù)不會(huì)中斷執(zhí)行中期的。

為了保證的可讀性,本文采用意譯而非直譯。

想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!

思考下面 JavaScript 代碼:
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");

控制臺(tái)打印的順序是怎樣的?

答案

正確的答案是:script start, script end, promise1, promise2, setTimeout,但是由于瀏覽器實(shí)現(xiàn)支持不同導(dǎo)致結(jié)果也不一致。

Microsoft Edge、Firefox 40、iOS Safari和桌面Safari 8.0.8 打印promise1promise2之前會(huì)先打印 setTimeout —— 這似乎是瀏覽器廠商相互競(jìng)爭(zhēng)導(dǎo)致的實(shí)現(xiàn)不同。這真的很奇怪,因?yàn)?Firefox 39 和 Safari 8.0.7 結(jié)果總是正確的。

為什么會(huì)這樣

要理解這一點(diǎn),需要了解事件循環(huán)如何處理任務(wù)和微任務(wù)。

每個(gè)“線程”都有自己的事件循環(huán),因此每個(gè) web worker 都有自己的事件循環(huán),因此可以獨(dú)立執(zhí)行,而來(lái)自同域的所有窗口共享一個(gè)事件循環(huán),所以它們可以同步地通信。

事件循環(huán)持續(xù)運(yùn)行,直到清空 Tasks 隊(duì)列的任務(wù)。一個(gè)事件循環(huán)有多個(gè)任務(wù)源,這些任務(wù)源保證了該源中的執(zhí)行順序(比如IndexedDB定義了它們自己的規(guī)范),但是瀏覽器可以在每次循環(huán)中選擇哪個(gè)源來(lái)執(zhí)行任務(wù)。這允許瀏覽器優(yōu)先選擇性能敏感的任務(wù),比如用戶輸入等。

Tasks 被放到任務(wù)源中,這樣瀏覽器就可以從內(nèi)部進(jìn)入JavaScript/DOM領(lǐng)域,并確保這些操作按順序進(jìn)行。在Tasks 執(zhí)行期間,瀏覽器可能更新渲染。從鼠標(biāo)點(diǎn)擊到事件回調(diào)需要調(diào)度一個(gè)任務(wù),解析超文本標(biāo)記語(yǔ)言也是如此。

setTimeout遲給定的時(shí)間,然后為它的回調(diào)調(diào)度一個(gè)新任務(wù)。這就是為什么setTimeout在打印script end之后打印,因?yàn)榇蛴?b>script end是第一個(gè)任務(wù)的一部分,而setTimeout在一個(gè)多帶帶的任務(wù)中。

微任務(wù)通常是針對(duì)當(dāng)前執(zhí)行腳本之后應(yīng)該立即發(fā)生的事情進(jìn)行調(diào)度的,比如對(duì)一批操作進(jìn)行響應(yīng),或者在不影響整個(gè)新任務(wù)的情況下進(jìn)行異步處理。

只要沒(méi)有其他JavaScript處于執(zhí)行中期,并且在每個(gè)任務(wù)的末尾,微任務(wù)隊(duì)列就在回調(diào)之后處理。在微任務(wù)期間排隊(duì)的任何其他微任務(wù)都會(huì)被添加到隊(duì)列的末尾并進(jìn)行處理。微任務(wù) 包括 MutationObserver callbacks。例如上面的例子中的 promisecallback

一個(gè)settled狀態(tài)的promise 或者已經(jīng)變成settled狀態(tài)(異步請(qǐng)求被settled)的promise,會(huì)立刻將它的callback(then)放到微任務(wù)隊(duì)列里面。

這確保了 promise 回調(diào)是異步的,即便promise已經(jīng)變?yōu)?b>settled狀態(tài)。因此一個(gè)已settledpromise調(diào)用.then(yey,nay)時(shí)將立即把一個(gè)微任務(wù)加入微任務(wù)隊(duì)列中。

這就是為什么promise1promise2會(huì)在script end后打印,因?yàn)楫?dāng)前運(yùn)行的腳本必須在處理微任務(wù)之前完成。promise1promise2setTimeout之前打印,因?yàn)槲⑷蝿?wù)總是在下一個(gè)任務(wù)之前發(fā)生。

好,一步一步的運(yùn)行:

瀏覽器之間會(huì)有什么不同?

一些瀏覽器的打印的順序是 script start, script end, setTimeout, promise1, promise2。它們?cè)?b>setTimeout之后運(yùn)行promise回調(diào)。很可能他們調(diào)用promise回調(diào)是作為新任務(wù)的一部分,而不是作為一個(gè)微任務(wù)。

這也是可以理解的,因?yàn)?b>promise來(lái)自 ECMAScript 而不是 HTML。ECMAScript 有“作業(yè)”的概念,類似于微任務(wù),但是除了模糊的郵件列表討論之外,這種關(guān)系并不明確。然而,普遍的共識(shí)是,promise應(yīng)該是微任務(wù)隊(duì)列的一部分并且有充足的理由。

promise 看作任務(wù)會(huì)導(dǎo)致性能問(wèn)題,因?yàn)榛卣{(diào)沒(méi)有必要因?yàn)槿蝿?wù)相關(guān)的事(比如渲染)而延遲執(zhí)行。它還會(huì)由于與其他任務(wù)源的交互而導(dǎo)致非確定性,并可能中斷與其他api的交互,稍后將詳細(xì)介紹。

這里有一條 Edge 反饋,它錯(cuò)誤地將 promises 當(dāng)作 任務(wù)。WebKit nightly 做對(duì)了,所以我認(rèn)為 Safari 最終會(huì)修復(fù),而 Firefox 43 似乎已經(jīng)修復(fù)。

如何判斷某些東西是否使用任務(wù)或微任務(wù)

動(dòng)手試一試是一種辦法,查看相對(duì)于promisesetTimeout如何打印,盡管這取決于實(shí)現(xiàn)是否正確。

一種方法是查看規(guī)范: 將一個(gè)任務(wù)加入隊(duì)列: step 14 of setTimeout

將 microtask 加入隊(duì)列:step 5 of queuing a mutation record

如上所述,ECMAScript 將微任務(wù)稱為作業(yè): 調(diào)用 EnqueueJob 將一個(gè) 微任務(wù)加入隊(duì)列:step 8.a of PerformPromiseThen

等級(jí)一 boss打怪

下面是一段html代碼:

給出下面的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
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);

在偷看答案前先試一試

試一試

和你猜想的有不同嗎?如果是,你得到的結(jié)果可能也是正確的。不幸的是,瀏覽器實(shí)現(xiàn)并不統(tǒng)一,下面是各個(gè)瀏覽器下測(cè)試結(jié)果:

誰(shuí)是正確的?

調(diào)度"click"事件是一項(xiàng)任務(wù)。 Mutation observer 和 promise 回調(diào)被列為微任務(wù)。 setTimeout 回調(diào)列為任務(wù)。 因此運(yùn)行過(guò)程如下:

所以 Chrome 是對(duì)的。對(duì)我來(lái)說(shuō)新發(fā)現(xiàn)是,微任務(wù)在回調(diào)之后運(yùn)行(只要沒(méi)有其它的 Javascript 在運(yùn)行),我原以為它只能在一個(gè)任務(wù)的末尾執(zhí)行。

瀏覽器出了什么問(wèn)題?

對(duì)于 mutation callbacks,F(xiàn)irefox 和 Safari 都正確地在內(nèi)部區(qū)域和外部區(qū)域單擊事件之間執(zhí)行完畢,清空了微任務(wù)隊(duì)列,但是 promises 列隊(duì)的處理看起來(lái)和chrome不一樣。這多少情有可原,因?yàn)樽鳂I(yè)和微任務(wù)的關(guān)系不清楚,但是我仍然期望在事件回調(diào)之間處理 Firefox ticket. Safari ticket.

對(duì)于 Edge,我們已經(jīng)看到它錯(cuò)誤的將 promises 當(dāng)作任務(wù),它也沒(méi)有在單擊回調(diào)之間清空微任務(wù)隊(duì)列,而是在所有單擊回調(diào)執(zhí)行完之后清空,于是總共只有一個(gè) mutate 在兩個(gè) click 之后打印。

等級(jí)一 boss打怪升級(jí)

仍然使用上面的例子,假如我們運(yùn)行下面代碼會(huì)怎么樣:

inner.click();

跟之前一樣,它會(huì)觸發(fā) click 事件,但這次是通過(guò) JS 調(diào)用的。

試一試

下面是各個(gè)瀏覽器的運(yùn)行情況:

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

為什么不同?

應(yīng)該是這樣的:

所以正確的順序是:click, click, promise, mutate, promise, timeout, timeout,似乎 Chrome 是對(duì)的。

以前,這意味著微任務(wù)在偵聽(tīng)器回調(diào)之間運(yùn)行,但.click()會(huì)導(dǎo)致事件同步調(diào)度,因此調(diào)用.click()的腳本仍然在回調(diào)之間的堆棧中。 上述規(guī)則確保微任務(wù)不會(huì)中斷執(zhí)行中期的JavaScript。 這意味著我們不處理偵聽(tīng)器回調(diào)之間的微任務(wù)隊(duì)列,它們?cè)趦蓚€(gè)偵聽(tīng)器之后處理。

總結(jié)

任務(wù)按順序執(zhí)行,瀏覽器可以在它們之間進(jìn)行渲染:

微任務(wù)按順序執(zhí)行,并執(zhí)行:

在每個(gè)回調(diào)之后,只要沒(méi)有其它代碼正在運(yùn)行。

在每個(gè)任務(wù)的末尾。

代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。

交流

干貨系列文章匯總?cè)缦?,覺(jué)得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。

https://github.com/qq44924588...

我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛(ài)好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!

關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。

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

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

相關(guān)文章

  • JS異步詳解 - 瀏覽器/Node/事件循環(huán)/消息隊(duì)列/宏任務(wù)/任務(wù)

    js異步歷史 一個(gè) JavaScript 引擎會(huì)常駐于內(nèi)存中,它等待著我們把JavaScript 代碼或者函數(shù)傳遞給它執(zhí)行 在 ES3 和更早的版本中,JavaScript 本身還沒(méi)有異步執(zhí)行代碼的能力,引擎就把代碼直接順次執(zhí)行了,異步任務(wù)都是宿主環(huán)境(瀏覽器)發(fā)起的(setTimeout、AJAX等)。 在 ES5 之后,JavaScript 引入了 Promise,這樣,不需要瀏覽器的安排,J...

    awesome23 評(píng)論0 收藏0
  • JavaScript 運(yùn)行機(jī)制--Event Loop詳解

    摘要:上代碼代碼可以看出,不僅函數(shù)比指定的回調(diào)函數(shù)先執(zhí)行,而且函數(shù)也比先執(zhí)行。這是因?yàn)楹笠粋€(gè)事件進(jìn)入的時(shí)候,事件環(huán)可能處于不同的階段導(dǎo)致結(jié)果的不確定。這是因?yàn)橐驗(yàn)閳?zhí)行完后,程序設(shè)定了和,因此階段不會(huì)被阻塞進(jìn)而進(jìn)入階段先執(zhí)行,后進(jìn)入階段執(zhí)行。 JavaScript(簡(jiǎn)稱JS)是前端的首要研究語(yǔ)言,要想真正理解JavaScript就繞不開他的運(yùn)行機(jī)制--Event Loop(事件環(huán)) JS是一門...

    snifes 評(píng)論0 收藏0
  • 前端基礎(chǔ)進(jìn)階(十二):深入核心,詳解事件循環(huán)機(jī)制

    摘要:前端基礎(chǔ)進(jìn)階正是圍繞這條線索慢慢展開,而事件循環(huán)機(jī)制,則是這條線索的最關(guān)鍵的知識(shí)點(diǎn)。特別是中正式加入了對(duì)象之后,對(duì)于新標(biāo)準(zhǔn)中事件循環(huán)機(jī)制的理解就變得更加重要。之后全局上下文進(jìn)入函數(shù)調(diào)用棧。 showImg(https://segmentfault.com/img/remote/1460000008811705); JavaScript的學(xué)習(xí)零散而龐雜,因此很多時(shí)候我們學(xué)到了一些東西,但...

    whjin 評(píng)論0 收藏0
  • Js 事件循環(huán)(Event Loop)機(jī)制以及實(shí)例講解

    摘要:主線程要明確的一點(diǎn)是,主線程跟執(zhí)行棧是不同概念,主線程規(guī)定現(xiàn)在執(zhí)行執(zhí)行棧中的哪個(gè)事件。主線程循環(huán)即主線程會(huì)不停的從執(zhí)行棧中讀取事件,會(huì)執(zhí)行完所有棧中的同步代碼。以上參考資料詳解中的事件循環(huán)機(jī)制中的事件循環(huán)運(yùn)行機(jī)制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...

    Anshiii 評(píng)論0 收藏0
  • 淺談不同環(huán)境下JavaScript執(zhí)行機(jī)制 + 示例詳解

    摘要:如果沒(méi)有其他異步任務(wù)要處理比如到期的定時(shí)器,會(huì)一直停留在這個(gè)階段,等待請(qǐng)求返回結(jié)果。執(zhí)行的執(zhí)行事件關(guān)閉請(qǐng)求的,例如事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過(guò)上述的階段。因此,才會(huì)早于執(zhí)行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(wù)(Synchronous) 在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù) ...

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

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

0條評(píng)論

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