一篇文章理解JS事件循環機制(JS Event Loop)

世界上最高效的學習方法,是把你學到的知識,經過本身理解的方式,教會另外一我的。共勉前端

 1. Event Loop

不少人都據說過 JS 是單線程的,可是線程是什麼?進程又是什麼?它們之間有什麼關係? promise

  從本質上說,這兩個名詞都是關於 CPU 時間片的一個描述。進程描述了 CPU 在運行指令、加載及保存上下文所需時間。線程則是進程中更小的一個單位,描述執行一段指令所需時間。 這個概念放到瀏覽器上說,打開一個 Tab 頁是一個進程,其中的 UI 渲染、JS 代碼執行、DOM 加載、Http 請求等都是一個個線程;放到 JS 代碼執行上來講,一個函數是一個進程,而函數內變量的定義、其餘函數的調用等任務都是一個個線程。瀏覽器

  JS 執行是單線程的,意味着 JS 在執行代碼的時候一次只能處理一個任務,必須按隊列順序逐個執行。JS 的主要功效是處理前端交互,其中就包括操做 DOM 節點。試想若 JS 是多線程,在處理網頁交互時,一個線程須要刪除 DOM 節點,另外一個線程倒是要操做同一個 DOM 節點,這樣該如何判斷先執行哪一個線程?但若隊列中存在多個任務,上一個任務的執行會阻塞下一個任務,致使代碼執行效率低下。就像 AJAX 請求線程,發出請求後須要等待響應結果,期間 CPU 倒是空閒的。對此,JS的事件循環機制(Event Loop)很好地解決了問題。bash

 2. 同步任務和異步任務

  JavaScript 將任務分爲兩種:同步任務和異步任務。多線程

  • 同步任務:執行完後能當即得出結果的任務。同步任務在主線程中執行,在執行過程當中產生堆棧,堆中存儲複雜數據類型(Object),棧中存儲基本數據類型(String、Number、Boolean、Null、Undefined、Symbol)。異步

  • 異步任務:執行後沒法當即得出結果,須要等待一段時間得到相應的任務。其中又分爲宏任務(Macrotask)和微任務(Microtask)函數

    宏任務:主程序ScriptsetTimeoutsetIntervalsetImmediateI/O操做(mouse click、keypress、network event)UI渲染requestAnimationTrame等。oop

    微任務:promiseMutationObserverprocess.nextTick()mutationObject.oberse等。學習

 3. 主線程、執行棧和隊列

 4. Event Loop

  1. 把同步任務推入主線程,異步任務推入任務隊列;
  2. 把主線程中的同步任務推入執行棧,開始執行;
  3. 執行棧爲空後,讀取任務隊列中的微任務,推入執行棧;
  4. 循環執行微任務隊列,直到微任務列表爲空;
  5. 讀取宏任務列表中的第一條宏任務,推入執行棧;
  6. 這時的宏任務又至關於一個線程,重複上述步驟1-5,直到任務隊列爲空。

 實例:ui

const a = 1;
const arr = [1, 2, 3];
console.log('1');
console.log(a, arr);

setTimeout(function () {
    console.log('2');
    process.nextTick(function () {
        console.log('3');
    });
    new Promise(function (resolve) {
        console.log('4');
        resolve();
    }).then(function () {
        console.log('5');
    });
}, 100);

new Promise(function (resolve) {
    console.log('6');
    resolve();
}).then(function () {
    console.log('7');
});

process.nextTick(function () {
    console.log('8');
    setImmediate(() => {
        console.info('9');
    });
    new Promise(function (resolve) {
        console.log('10');
        resolve();
    }).then(function () {
        console.log('11');
    });
    setTimeout(function () {
        console.log('12');
        setImmediate(() => {
            console.info('13');
        });
        process.nextTick(function () {
            console.log('14');
        });
        new Promise(function (resolve) {
            console.log('15');
            resolve();
        }).then(function () {
            console.log('16');
        });
    }, 100);
    process.nextTick(function () {
        console.log('17');
    });
});

執行結果:1  1,[1,2,3]  6 7 8 10 17 11 9 2 4  5 3 12 15 16 13 14

複製代碼

 解析:

 上述代碼就至關於一個進程,裏面的函數執行即爲一個個線程,一個個任務。

  1. 把同步任務推入主線程,異步任務推入任務隊列;
  2. 把主線程中的同步任務推入執行棧,開始執行;得出結果1 1,[1,2,3]
  3. 執行棧爲空後,讀取任務隊列中的微任務,推入執行棧;得出結果6 7
  4. 循環執行微任務隊列,直到微任務列表爲空;獲得結果 8 10 17 11 9
  5. 讀取宏任務列表中的第一條宏任務,推入執行棧; 獲得結果 2 4 5 3
  6. 這時的宏任務又至關於一個線程,重複上述步驟1-5,直到任務隊列爲空。
相關文章
相關標籤/搜索