摘要:眾所周知和都屬于上述異步任務(wù)的一種那到底為什么和會(huì)有順序之分這就是我想分析總結(jié)的問(wèn)題所在了和的作用是為了讓瀏覽器能夠從內(nèi)部獲取的內(nèi)容并確保執(zhí)行棧能夠順序進(jìn)行。只要執(zhí)行棧沒(méi)有其他在執(zhí)行,在每個(gè)結(jié)束時(shí),隊(duì)列就會(huì)在回調(diào)后處理。
前言
我是在做前端面試題中看到了setTimeout和Promise的比較,然后第一次看到了microtask和macrotask的概念,在閱讀了一些文章之后發(fā)現(xiàn)沒(méi)有一個(gè)比較全面易懂的文章,所以我嘗試做一個(gè)梳理性的總結(jié).
這道經(jīng)典的面試題引起了我的興趣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");JavaScript的事件循環(huán)機(jī)制
首先我們先弄清楚setTimeout和Promise的共同點(diǎn),也就是我第一次的看到那道面試題的疑惑點(diǎn).
JavaScript 主線(xiàn)程擁有一個(gè) 執(zhí)行棧 以及一個(gè) 任務(wù)隊(duì)列,主線(xiàn)程會(huì)依次執(zhí)行代碼,當(dāng)遇到函數(shù)時(shí),會(huì)先將函數(shù) 入棧,函數(shù)運(yùn)行完畢后再將該函數(shù) 出棧,直到所有代碼執(zhí)行完畢。
上面的例子的執(zhí)行棧執(zhí)行順序應(yīng)該是這樣的
console.log("script start"); console.log("script end"); Promise.resolve();
而任務(wù)隊(duì)列的執(zhí)行順序應(yīng)該是這樣的
Promise.then(function() { console.log("promise1"); }); Promise.then(function() { console.log("promise2"); }); setTimeout(function() { console.log("setTimeout"); }, 0);
而主線(xiàn)程則會(huì)在 清空當(dāng)前執(zhí)行棧后,按照先入先出的順序讀取任務(wù)隊(duì)列里面的任務(wù)。
眾所周知setTimeout和Promise.then()都屬于上述異步任務(wù)的一種,那到底為什么setTimeout和Promise.then()會(huì)有順序之分,這就是我想分析總結(jié)的問(wèn)題所在了.
macrotasks(tasks) 和 microtasks taskstasks的作用是為了讓瀏覽器能夠從內(nèi)部獲取javascript / dom的內(nèi)容并確保執(zhí)行棧能夠順序進(jìn)行。
tasks的調(diào)度是隨處可見(jiàn)的,例如解析HTML,獲得鼠標(biāo)點(diǎn)擊的事件回調(diào)等等,在這個(gè)例子中,我們所迷惑的setTimeout也是一個(gè)tasks.
microtasksmicrotasks通常用于在當(dāng)前正在執(zhí)行的腳本之后直接發(fā)生的事情,比如對(duì)一系列的行為做出反應(yīng),或者做出一些異步的任務(wù),而不需要新建一個(gè)全新的tasks。
只要執(zhí)行棧沒(méi)有其他javascript在執(zhí)行,在每個(gè)tasks結(jié)束時(shí),microtasks隊(duì)列就會(huì)在回調(diào)后處理。在microtasks期間排隊(duì)的任何其他microtasks將被添加到這個(gè)隊(duì)列的末尾并進(jìn)行處理。
microtasks包括mutation observer callbacks,就像上例中的promise callbacks一樣。
所以上面的例子執(zhí)行順序的實(shí)質(zhì)是tasks =>start end以及resolve
microtasks =>promise1和promise2
tasks =>setTimeout
具體應(yīng)用需要注意的是,在兩個(gè)tasks之間,瀏覽器會(huì)重新渲染。這也是我們需要了解tasks和microtasks的一個(gè)非常重要的原因.
Vue 中如何使用 MutationObserver 做批量處理? - 顧軼靈的回答 - 知乎瀏覽器兼容問(wèn)題根據(jù) HTML Standard,在每個(gè) task 運(yùn)行完以后,UI 都會(huì)重渲染,那么在 microtask 中就完成數(shù)據(jù)更新,當(dāng)前 task 結(jié)束就可以得到最新的 UI 了。反之如果新建一個(gè) task 來(lái)做數(shù)據(jù)更新,那么渲染就會(huì)進(jìn)行兩次。
在__Microsoft Edge__, Firefox 40__, __iOS Safari 以及 desktop Safari 8.0.8 中setTimeout會(huì)先于Promise
該例子來(lái)自Jake Archibald-->Tasks, microtasks, queues and schedules,其中有動(dòng)畫(huà)來(lái)展現(xiàn)tasks和microtasks的具體工作流程,十分推薦閱讀//html
// 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);
在這個(gè)例子中,不同瀏覽器的log是不同的,如下所示
Chrome | Firefox | Safari | edge |
---|---|---|---|
click | click | click | click |
promise | mutate | mutate | click |
mutate | click | click | mutate |
click | mutate | mutate | timeout |
promise | timeout | promise | promise |
mutate | promise | promise | timeout |
timeout | promise | timeout | promise |
timeout | timeout | timeout |
事實(shí)上Chrome是正確的,而且由此可發(fā)現(xiàn)microtasks并不是在tasks的結(jié)束階段開(kāi)始執(zhí)行,而是在tasks中回調(diào)結(jié)束之后(只要沒(méi)有正在執(zhí)行的JavaScript代碼)
總結(jié)tasks會(huì)順序執(zhí)行,瀏覽器會(huì)在執(zhí)行間隔重新渲染
microtasks會(huì)順序執(zhí)行,執(zhí)行時(shí)機(jī)為
在沒(méi)有JavaScript代碼執(zhí)行的callback之后在每一個(gè)tasks之后
由于我是前端初學(xué)者,對(duì)于JavaScript還很不熟悉,對(duì)事件循環(huán)的進(jìn)程模型不是很了解,希望這篇文章能夠幫助大家.
事件循環(huán)機(jī)制建議參考文章
阮一峰-->JavaScript 運(yùn)行機(jī)制詳解:再談Event LoopHTML Living Standard — Last Updated 9 April 2018
tasks建議參考文章
Jake Archibald-->Tasks, microtasks, queues and schedules理解 JavaScript 中的 macrotask 和 microtask
setImmediate.js --A YuzuJS production
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107676.html
摘要:常見(jiàn)應(yīng)用則是為了完成一些更新應(yīng)用程序狀態(tài)的較小的任務(wù),如處理的回調(diào)和的修改,以便讓這些任務(wù)在瀏覽器重新渲染之前執(zhí)行。常見(jiàn)應(yīng)用執(zhí)行順序的實(shí)現(xiàn)需要至少一個(gè)和至少一個(gè)。 簡(jiǎn)介 我們?cè)谏弦黄?《淺析 JS 中的EventLoop 事件循環(huán)》 中提到一個(gè) Event Queue,其實(shí)在事件循環(huán)中 queue 一共有兩種,還有一種叫 Job Queue 其中 Event Queue 在 HTML...
摘要:主線(xiàn)程會(huì)暫時(shí)存儲(chǔ)等異步操作,直接向下執(zhí)行,當(dāng)某個(gè)異步事件觸發(fā)時(shí),再通知主線(xiàn)程執(zhí)行相應(yīng)的回調(diào)函數(shù),通過(guò)這種機(jī)制,避免了單線(xiàn)程中異步操作耗時(shí)對(duì)后續(xù)任務(wù)的影響。 背景 在研究js的異步的實(shí)現(xiàn)方式的時(shí)候,發(fā)現(xiàn)了JavaScript 中的 macrotask 和 microtask 的概念。在查閱了一番資料之后,對(duì)其中的執(zhí)行機(jī)制有所了解,下面整理出來(lái),希望可以幫助更多人。 先了解一下js的任務(wù)執(zhí)...
摘要:的回調(diào)函數(shù)正是處于隊(duì)列之中。將看做會(huì)導(dǎo)致性能問(wèn)題,回調(diào)函數(shù)可能會(huì)因?yàn)殇秩镜认嚓P(guān)產(chǎn)生不必要的延后。瀏覽器是怎么出錯(cuò)的和在兩次點(diǎn)擊操作之間運(yùn)行完成了所有的,就比如的回調(diào)函數(shù)所展示的,但是似乎有不同的排序算法。 帶有可視代碼執(zhí)行順序的原文鏈接https://jakearchibald.com/201...,此篇文字并非其完整翻譯,加入了一部分自己的理解,比如將其中的task替換為macrot...
摘要:的回調(diào)函數(shù)正是處于隊(duì)列之中。將看做會(huì)導(dǎo)致性能問(wèn)題,回調(diào)函數(shù)可能會(huì)因?yàn)殇秩镜认嚓P(guān)產(chǎn)生不必要的延后。瀏覽器是怎么出錯(cuò)的和在兩次點(diǎn)擊操作之間運(yùn)行完成了所有的,就比如的回調(diào)函數(shù)所展示的,但是似乎有不同的排序算法。 帶有可視代碼執(zhí)行順序的原文鏈接https://jakearchibald.com/201...,此篇文字并非其完整翻譯,加入了一部分自己的理解,比如將其中的task替換為macrot...
前言 我在學(xué)習(xí)瀏覽器和NodeJS的Event Loop時(shí)看了大量的文章,那些文章都寫(xiě)的很好,但是往往是每篇文章有那么幾個(gè)關(guān)鍵的點(diǎn),很多篇文章湊在一起綜合來(lái)看,才可以對(duì)這些概念有較為深入的理解。 于是,我在看了大量文章之后,想要寫(xiě)這么一篇博客,不采用官方的描述,結(jié)合自己的理解以及示例代碼,用最通俗的語(yǔ)言表達(dá)出來(lái)。希望大家可以通過(guò)這篇文章,了解到Event Loop到底是一種什么機(jī)制,瀏覽器和Nod...
閱讀 5792·2021-11-24 10:25
閱讀 2715·2021-11-16 11:44
閱讀 3865·2021-10-11 11:09
閱讀 3184·2021-09-02 15:41
閱讀 3269·2019-08-30 14:14
閱讀 2297·2019-08-29 14:10
閱讀 2359·2019-08-29 11:03
閱讀 1136·2019-08-26 13:47