每次我在寫技術類文章的時候都喜歡用引用一個神話故事或者一位超級英雄。沒錯,由於個人中二病很嚴重,寫代碼的時候都幻想本身有一對機械手臂幫我在那啪啪啪的調試bug,別想歪了不是那種啪啪啪。 此次我要說的就是 蟻人html
好吧,爲何要說蟻人那。若是你看過漫威(雖然我是DC粉)的超級英雄電影你應該知道蟻人的能力。變大 🐜 --- 變大 🐜 --- 變大 🐜--- 變大 🐜 --- 變大 🐜html5
爲何這麼說那?由於你想在<美隊3>裏蟻人內戰之中變得很大,可是問題就來了,變大以後雖然力量有了加成,可是速度變得很慢,我如今不說你應該猜出宏任務的缺點了吧? 同理,蟻人變小以後靈巧了很多,能夠跟不少螞蟻溝通,這些螞蟻井井有理幫助蟻人,是否是這些小🐜很像微任務,那麼微任務的好處你也猜到了吧?node
咱們來引入谷歌的一位大神 Jake 的文章做爲說明,原文標題:《Tasks, microtasks, queues and schedules》原文地址:Tasks, microtasks, queues and scheduleschrome
首先看一段代碼:segmentfault
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');
複製代碼
打印順序是什麼?(ctrl+A查看答案)promise
正確答案是: script start, script end, promise1, promise2, setTimeout瀏覽器
這是爲何那?bash
我稍稍解釋下,既然我說了,宏任務是變大那麼效率確定低,微任務反之確定是有序進行效率確定要高一些,那麼咱們能看出,SetTimeout確定是效率低的。多線程
由於SetTimeout等於在開一條多線程幹另外一件事,那麼SetTimeout是宏任務無疑的。與之相反的異步處理方式固然是微任務了。dom
也就是說Promise對象返回的狀態給了then,then就是那些小螞蟻,這些小螞蟻會有條不紊的工做下去,.then().then().then().then()的鏈式調用,只要你願意就會無窮無盡。
那麼這個問題好解了,因此 'script start'確定會被先打印出來,script end隨後打印出來,SetTimeout先日後放由於效率低,而後'promise1'打印,'promise2'打印 兩個微任務執行完畢,最後安排完小螞蟻后蟻人變大 觸發SetTimeout最後執行。
由於SetTimeout這種宏任務很容易在觸犯的時候關聯着DOM元素的變化,SetTimeout不能本身完成須要等待DOM元素修改後的結果,這樣每次觸發任務以後還要關聯DOM元素的變化,效率確定要低不少。promise就不同了,每次執行的微任務都被螞蟻排着隊處理了,每一個螞蟻都有條不紊的一個接一個進行處理,這些螞蟻就造成了一個任務隊列。也就是說你們執行的任務都是有順序的,若是在執行任務的過程當中有新的微任務產生就日後排,等待前面的螞蟻執行完畢,再去執行新的任務。
可是理想是牆上的美女,現實是炕上的媳婦... 總有些東西會無情的打你臉...
理論是這個理論,實踐倒是另外一種實踐。有的瀏覽器打印結果就是不同,啪啪啪打的響不響?
有些瀏覽會會打印出: script start, script end, setTimeout, promise1, promise2。
他們會在setTimeout以後執行promise的回調,就好像這些瀏覽器會把promise的回調視做一個新的宏任務而不是微任務。
其實無可厚非,由於promises 來自於ECMAScript 的標準而不是HTML標準。 ECMAScript 有個關於jobs的概念和微任務挺相似的,可是否明確具備關聯關係卻還沒有定論(相關討論)。然而,廣泛的觀點是promise應該屬於微任務。
簡單來講就是各個瀏覽器廠商大哥相互之間達不成共識,結果對宏任務和微任務的理解各有差別,這點不只瀏覽器,就連node的打印結果和瀏覽器之間的版本都不必定想通!
來點官方的說法吧~我是不肯意看啊
若是說把 promise 當作一個新的 task 來執行的話,這將會形成一些性能上的問題,由於 promise 的回調函數可能會被延遲執行,由於在每個 task 執行結束後瀏覽器可能會進行一些渲染工做。因爲做爲一個 task 將會和其餘任務來源(task source)相互影響,這也會形成一些不肯定性,同時這也將打破一些與其餘 API 的交互,這樣一來便會形成一系列的問題。
繼續看下面的代碼 一段容器
<div class="outer">
<div class="inner"></div>
</div>
複製代碼
接着看觸發的任務
// 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 event是一個宏任務,Mutation observer和promise都會進入微任務隊列,setTimeout回調是一個宏任務,建議原文觀看 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
瀏覽器哪裏出錯了?
Firefox和Safari在click監聽器回調之間正確執行了mutation 回調的微任務,但promise打印結果卻出如今了錯誤的位置。 無可厚非的是jobs和微任務的關係太含糊不清,不過我仍認爲應該在click監聽器回調之間執行。 Edge咱們早就知道會把promise回調放進錯誤的隊列,但他也也沒在click監聽器回調之間執行微任務隊列,而是在全部監聽器回調後執行,這打印click以後只打印了一次muteta,所以這是Edge的一個bug。
而後能夠總結了,我我的以爲結果以chrome的結果做爲標準就能夠。