js 宏任務和微任務

寫在前面:壓力只是暫時的,都會過去,這是我一週覺得聽到的最頓悟的一句話了吧~javascript

  1.引言php

  js做爲單線程的運行機制,一定有本身的運行順序,在聽了一次分享後,也好奇這種運行的機制究竟是什麼?html

  js可分爲同步任務和異步任務,對於同步的任務,咱們固然知道按照順序進行執行,可是對於異步的操做,會有一個優先級的執行順序,分別爲宏任務和微任務vue

宏任務(macrotasks)和微任務(microtasks)??包含什麼?java

    macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
    microtasks: process.nextTick, Promises, Object.observe(廢棄), MutationObserver

 在js執行時候,一個主線程裏面都會有一個事件循環(消息循環|運行循環)和事件隊列,存放各類要處理的事件信息,經過這個循環不斷處理這些事件信息或消息。web

談到這裏,很明顯知道,其實出現宏任務和微任務和瀏覽器以及js的執行機制有很大的關係。瞭解js的執行機制promise

2.javascript的執行runtime瀏覽器

 

 

 這是一張javascript的執行機制圖多線程

JavaScript的運行分爲

  (1) javaScript Engine,Chrome 的引擎就是 V8異步

  (2) Web APIs,DOM 的操做,AJAX,Timeout 等實際上調用的都是這裏提供的

  (3)Callback Queue,回調的隊列,也就是剛剛全部的 Web APIs 裏面的回調函數,實際上都是放在這裏排隊的

  (4) EventLoop,事件循環,也就是剛所說的宏任務和微任務的容器

call Stack

      自己就是一個調用棧(就像瀏覽器中的JavaScript解釋器),追蹤函數執行流的一種機制,當執行環境調用了多個函數時,經過調用棧,咱們能夠追蹤到哪個函數在執行,執行的函數體中又調用了哪些函數。

             每調用一個函數,解釋器就會把該函數添加進調用棧並開始執行。

            正在調用棧中執行的函數還調用了其它函數,那麼新函數也將會被添加進調用棧,一旦這個函數被調用,便會當即執行。

             當前函數執行完畢後,解釋器將其清出調用棧,繼續執行當前執行環境下的剩餘的代碼。

             當分配的調用棧空間被佔滿時,會引起「堆棧溢出」。

    是存放執行的重要條件,也是由於只有一個調用棧,因此被稱爲單線程

callback quene

   在js的編譯階段,將一些時間防止在執行隊列中

EventLoop 事件循環

    一個做用就是將callback quene隊列裏的執行事件放在在call stack中,執行

3.事件循環

  js是單線程的,執行較長的js時候,頁面會卡死,沒法響應,可是全部的操做都會被記住到另外的隊列。好比:點擊了一個元素,不會馬上的執行,可是等到js加載完畢後就會執行剛纔點擊的操做,可以知道有一個隊列記錄了全部有待執行的操做,這個隊列分爲微觀和宏觀。微觀會比宏觀執行得更快。

   事件循環就是多線程的一種工做方式,Chrome裏面是使用了共享的task_runner對象給本身和其它線程post task過來存起來,用一個死循環不斷地取出task執行,或者進入休眠等待被喚醒。Mac的Chrome渲染線程和瀏覽器線程還藉助了Mac的sdk Cococa的NSRunLoop來作爲UI事件的消息源。Chrome的多進程通訊(不一樣進程的IO線程的本地socket通訊)藉助了libevent的事件循環,並加入了到了主消息循環裏面。

  稱爲事件循環的緣由大多來源於源碼

 

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

  能夠看到入伏哦有消息就會執行,沒消息就會繼續等待

消息執行完成
因爲js單線程,每個消息完整的執行後纔會執行其餘的消息,不會被其餘的消息搶佔,這樣的缺點就是當一個消息執行的特別久的時候web用戶沒法處理用戶的交互,好比點擊或者滾動等,所以須要縮短消息的處理時間,將一個消息裁剪成多個消息。
添加消息
在瀏覽器裏,當一個事件發生且有一個事件監聽器綁定在該事件上時,消息會被隨時添加進隊列。若是沒有事件監聽器,事件會丟失。因此點擊一個附帶點擊事件處理函數的元素會添加一個消息,其它事件相似。好比事件委託,監聽,可隨時放入消息隊列
setTimeout 接受一個參數,待加入隊列的消息和一個延遲,延遲表明了消息加入隊列的最小延遲事件,若是隊列中沒有其餘的消息,在這段延遲時間過了後,消息會被馬上處理,可是若是有其餘的消息,必須等到其餘的消息處理完,所以,延遲時間表示最少延遲時間
 

 

4.宏任務和微任務 

  是一種人們定義的執行名字,

 

如何區分宏任務和微任務呢?劃分的標準是什麼

宏任務本質:參與了事件循環的任務。
回到 Chromium 中,須要處理的消息主要分紅了三類:
  • Chromium 自定義消息
  • Socket 或者文件等 IO 消息
  • UI 相關的消息
1. 與平臺無關的消息,例如 setTimeout 的定時器就是屬於這個
2.Chromium 的 IO 操做是基於 libevent 實現,它自己也是一個事件驅動的庫
3.UI 相關的其實屬於 blink 渲染引擎過來的消息,例如各類 DOM 的事件
其實與 JavaScript 的引擎無關,都是在 Chromium 實現的。

微任務本質:直接在 Javascript 引擎中的執行的,沒有參與事件循環的任務。
  1. 是個內存回收的清理任務,使用過 Java 的童鞋應該都很熟悉,只是在 JavaScript 這是V8內部調用的
  2. 就是普通的回調,MutationObserver 也是這一類
  3. Callable
  4. 包括 Fullfiled 和 Rejected 也就是 Promise 的完成和失敗
  5. Thenable 對象的處理任務
宏任務,微任務的優先級
promise 是在當前腳本代碼執行完後,馬上執行的,它並無參與事件循環,因此它的優先級是高於 setTimeout。
宏任務和微任務的總結:
  • 宏任務 Macrotasks 就是參與了事件循環的異步任務。
  • 微任務 Microtasks 就是沒有參與事件循環的「異步」任務。
  • console.log("開始執行1")
    console.log(Object.keys({a: 1}));
    setTimeout(() => {
        console.log(Object.keys({b: 2}));
        var promise = new Promise((resolve, reject) => {
            resolve(1);
        });
        promise.then(res => {
            //後續增長測試
            setTimeout(() => {
                console.log(Object.keys({e:1}))
            }, 0);
            console.log(Object.keys({c: 1}));
        });
    }, 2000);
    console.log("結束執行2")
    

      

     

     微觀任務是在當前JS調用執行完了以後馬上執行的,是同步的,在同一個調用棧裏,沒有多線程異步,如這裏包括promise.then在內的setTimeout回調裏的代碼都是在DOMTimer.Fired執行的,只是說then被放到了當前要執行的整一個異步回調函數的最後面執行。因此setTimeout 0是給主線程的消息循環任務隊列添加了一個新的task(回調),而promise.then是在當前task的V8裏的microtask插入了一個任務。那麼確定是當前正在執行的task執行完了才執行下一個task.vue的nextTick 也是一個微觀任務

除此以外的onload事件
let img = new Image(); 
img.src = 'image01.png?_=' + Date.now(); 
img.onload = function () { console.log('img ready'); } console.log(Object.keys({e: 1}));
微觀任務是不屬於事件循環的,它是V8的一個實現,用來實現Promise的then/reject,以及其它一些須要同步延後的callback,本質上它和當前的V8調用棧是同步執行的,只是放到了最後面。除了Promise/MutationObserver,在JS裏面發起的請求也會建立一個微觀任務延後執行。
參考網站:
相關文章
相關標籤/搜索