圖解JS執行機制

大體流程


咱們能夠看出:

  1. js代碼分爲同步任務和異步任務。
  2. 同步任務會進入主線程,異步任務會進入Event Table(事件表),當事件表中的異步任務完成後會在Event Queue(事件隊列)中註冊回調函數。
  3. 主線程任務所有完成後,纔會完成Event Queue中的任務。
  4. js解析器會不斷地重複檢查主線程執行棧是否爲空,而後重複第3步,這就是Event Loop(事件循環)。

js代碼的類型分爲:ajax

注意promise

  1. 任務又能夠進一步分爲宏任務和微任務,這對js代碼的執行有更爲細緻的影響,在文章下面會有講解。
  2. 異步任務中的宏任務和微任務會進入不一樣的Event Queue事件隊列,即Event Queue又能夠分爲宏任務隊列和微任務隊列。
  3. setInterval會按照設定的時間間隔重複地在Event Queue註冊回調函數,若是某一段時間主線程代碼執行過久,那麼setInterval的回調函數可能阻塞到一塊兒執行,沒法保持設定的時間間隔,若是此時setInterval用於動畫,則體現爲卡頓。

詳細流程


在事件循環(主線程 → 事件隊列)中其實有更細緻的操做流程,即(宏任務 → 微任務)之間的循環,以下圖所示:bash

因此一般來講,咱們頁面中的js執行順序是這樣的:

  • 第一輪事件循環:
  1. 主線程執行js整段代碼(宏任務),將ajax、setTimeout、promise等回調函數註冊到Event Queue,並區分宏任務和微任務。
  2. 主線程提取並執行Event Queue 中的ajax、promise等全部微任務,並註冊微任務中的異步任務到Event Queue。
  • 第二輪事件循環:
  1. 主線程提取Event Queue 中的第一個宏任務(一般是setTimeout)。
  2. 主線程執行setTimeout宏任務,並註冊setTimeout代碼中的異步任務到Event Queue(若是有)。
  3. 執行Event Queue中的全部微任務,並註冊微任務中的異步任務到Event Queue(若是有)。
  • 相似的循環:宏任務每執行完一個,就清空一次事件隊列中的微任務

注意:事件隊列中分「宏任務隊列」和「微任務隊列」,每執行一次任務均可能註冊新的宏任務或微任務到相應的任務隊列中,只要遵循「每執行一個宏任務,就會清空一次事件隊列中的全部微任務」這一循環規則,就不會弄亂。異步


實例


實例1

console.log('1');
// 1 6 7 2 4 5 9 10 11 8 3
// 記做 set1
setTimeout(function () {
    console.log('2');
    // set4
    setTimeout(function() {
        console.log('3');
    });
    // pro2
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5')
    })
})

// 記做 pro1
new Promise(function (resolve) {
    console.log('6');
    resolve();
}).then(function () {
    console.log('7');
    // set3
    setTimeout(function() {
        console.log('8');
    });
})

// 記做 set2
setTimeout(function () {
    console.log('9');
    // 記做 pro3
    new Promise(function (resolve) {
        console.log('10');
        resolve();
    }).then(function () {
        console.log('11');
    })
})
複製代碼
  • 第一輪事件循環:
  1. 總體script做爲第一個宏任務進入主線程,遇到console.log,輸出1。

2. 遇到set1,其回調函數被分發到宏任務Event Queue中。

3. 遇到pro1,new Promise直接執行,輸出6。then被分發到微任務Event Queue中。

4. 遇到了set2,其回調函數被分發到宏任務Event Queue中。

5. 主線程的整段js代碼(宏任務)執行完,開始清空全部微任務;主線程執行微任務pro1,輸出7;遇到set3,註冊回調函數。

  • 第二輪事件循環:
  1. 主線程執行隊列中第一個宏任務set1,輸出2;代碼中遇到了set4,註冊回調;又遇到了pro2,new promise()直接執行輸出4,並註冊回調;

2. set1宏任務執行完畢,開始清空微任務,主線程執行微任務pro2,輸出5。

  • 第三輪事件循環
  1. 主線程執行隊列中第一個宏任務set2,輸出9;代碼中遇到了pro3,new promise()直接輸出10,並註冊回調;
  2. set2宏任務執行完畢,開始狀況微任務,主線程執行微任務pro3,輸出11。
  • 相似循環...

因此最後輸出結果爲一、六、七、二、四、五、九、十、十一、八、3函數

相關文章
相關標籤/搜索