天天看點

js event Loop的機制

enent Loop 即時事件循環,在浏覽器或者node環境下js單線程運作時不會阻塞的一種機制,也就是我們所了解的異步原理

js的運作機制是分為堆、棧和隊列,如圖所示:

js event Loop的機制

而在js機制中,js是分為宏任務(MacroTask)也叫Task和微任務(MicroTask)的,浏覽器在運作的時候會首先清空微任務,然後才會執行宏任務。

mask宏任務:

script全部代碼、setTimeout、setInterval、setImmediate(浏覽器暫時不支援,隻有IE10支援,具體可見MDN)、I/O、UI Rendering

MicroTask微任務:

Process.nextTick(Node獨有,vue裡面有用)、Promise、Object.observe(廢棄)、MutationObserver,詳情可檢視這裡

舉例如下

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');
        注意:setTimeout是宏任務,Promise是微任務(如果是new Promise則是宏任務)
           

第一次執行:

執行同步代碼,将宏任務(Tasks)和微任務(Microtasks)劃分到各自隊列中。

log:script start、script end

第二次執行:

執行宏任務後,檢測到微任務(Microtasks)隊列中不為空,執行Promise1,執行完成Promise1後,調用Promise2.then,放入微任務(Microtasks)隊列中,再執行Promise2.then。

log:script start、script end、promise1、promise2

第三次執行:

當微任務(Microtasks)隊列中為空時,執行宏任務(Tasks),執行setTimeout callback,列印日志。

log:script start、script end、promise1、promise2、setTimeout

console.log('script start')  //1

        async function async1() {
            await async2()
            console.log('async1 end') //5
        }
        async function async2() {
            console.log('async2 end') //2
        }
        async1()

        setTimeout(function () {
            console.log('setTimeout') //8
        }, 0)

        new Promise(resolve => {
            console.log('Promise') //3
            resolve()
        })
            .then(function () {
                console.log('promise1') //6
            })
            .then(function () {
                console.log('promise2') //7
            })

        console.log('script end')//4
           

首先,列印script start,調用async1()時,傳回一個Promise,是以列印出來async2 end。

每個 await,會新産生一個promise,但這個過程本身是異步的,是以該await後面不會立即調用。

繼續執行同步代碼,列印Promise和script end,将then函數放入微任務隊列中等待執行。

同步執行完成之後,檢查微任務隊列是否為null,然後按照先入先出規則,依次執行。

然後先執行列印promise1,此時then的回調函數傳回undefinde,此時又有then的鍊式調用,又放入微任務隊列中,再次列印promise2。

再回到await的位置執行傳回的 Promise 的 resolve 函數,這又會把 resolve 丢到微任務隊列中,列印async1 end。

當微任務隊列為空時,執行宏任務,列印setTimeout。

Process.nextTick()(vue中就是使用這個來進行插隊的)

let bar;

setTimeout(() => {
  console.log('setTimeout');
}, 0)

setImmediate(() => {
  console.log('setImmediate');
})
function someAsyncApiCall(callback) {
  process.nextTick(callback);
}

someAsyncApiCall(() => {
  console.log('bar', bar); // 1
});

bar = 1;
           

結果:

bar 1

setTimeout

setImmediate

首先執行

process.nextTick(callback);