事件迴圈進階:macrotask與microtask
這段參考了參考來源中的第2篇文章(英文版的),(加了下自己的理解重新描述了下),
這裡沒法給大家演示程式碼,我就簡單說下我的理解吧。
promise和settimeout 在一起的時候執行順序是個有意思的事兒,
為什麼呢?因為Promise裡有了一個一個新的概念:microtask
或者,進一步,JS中分為兩種任務型別:macrotask
和microtask
,在ECMAScript中,microtask稱為jobs
,macrotask可稱為task
它們的定義?區別?簡單點可以按如下理解:
-
macrotask(又稱之為巨集任務),可以理解是每次執行棧執行的程式碼就是一個巨集任務(包括每次從事件佇列中獲取一個事件回撥並放到執行棧中執行)
-
每一個task會從頭到尾將這個任務執行完畢,不會執行其它
-
瀏覽器為了能夠使得JS內部task與DOM任務能夠有序的執行,會在一個task執行結束後,在下一個 task 執行開始前,對頁面進行重新渲染
(`task->渲染->task->...`)
-
microtask(又稱為微任務),可以理解是在當前 task 執行結束後立即執行的任務
-
也就是說,在當前task任務後,下一個task之前,在渲染之前
-
所以它的響應速度相比setTimeout(setTimeout是task)會更快,因為無需等渲染
-
也就是說,在某一個macrotask執行完後,就會將在它執行期間產生的所有microtask都執行完畢(在渲染前)
分別很麼樣的場景會形成macrotask和microtask呢?
-
macrotask:主程式碼塊,setTimeout,setInterval等(可以看到,事件佇列中的每一個事件都是一個macrotask)
-
microtask:Promise,process.nextTick等
__補充:在node環境下,process.nextTick的優先順序高於Promise__,也就是可以簡單理解為:在巨集任務結束後會先執行微任務佇列中的nextTickQueue部分,然後才會執行微任務中的Promise部分。
再根據執行緒來理解下:
-
macrotask中的事件都是放在一個事件佇列中的,而這個佇列由事件觸發執行緒維護
-
microtask中的所有微任務都是新增到微任務佇列(Job Queues)中,等待當前macrotask執行完畢後執行,而這個佇列由JS引擎執行緒維護(這點由自己理解+推測得出,因為它是在主執行緒下無縫執行的)
所以,總結下執行機制:
-
執行一個巨集任務(棧中沒有就從事件佇列中獲取)
-
執行過程中如果遇到微任務,就將它新增到微任務的任務佇列中
-
巨集任務執行完畢後,立即執行當前微任務佇列中的所有微任務(依次執行)
-
當前巨集任務執行完畢,開始檢查渲染,然後GUI執行緒接管渲染
-
渲染完畢後,JS執行緒繼續接管,開始下一個巨集任務(從事件佇列中獲取)
-
另外,請注意下
Promise
的polyfill
與官方版本的區別:-
官方版本中,是標準的microtask形式
-
polyfill,一般都是通過setTimeout模擬的,所以是macrotask形式
-
請特別注意這兩點區別
注意,有一些瀏覽器執行結果不一樣(因為它們可能把microtask當成macrotask來執行了),
但是為了簡單,這裡不描述一些不標準的瀏覽器下的場景(但記住,有些瀏覽器可能並不標準) -
好了,到這裡咱們這個從瀏覽器到多程序到多執行緒到js單執行緒到js執行機制就講完了。內容還算是比較全面,不足的就是隻能聽麼有程式碼參考。
不過沒關係,大家可以根據我提到的知識點自己再進行深入的學習,或者關注下我個人的公眾號-重度前端,我講的內容都有文字版在上面而且有程式碼,另外我記錄了一些我平時看到的比較好的 有深度的文章,還有一些原創內容。直接微信 搜 重度前端。
好了,今天就到這裡了。後面計劃下分享哪些有意思的東西,咱們下次見。