node中的事件環(Event Loop)

周必川發表於2018-04-06

node中的事件環(Event Loop)

這裡的一張圖是js中事件執行的順序圖,從圖中我們可以看到我們寫的一段程式碼,執行的時候首先會進入執行棧,從上往下執行。

如:

console.log(1);
new Promise(function(resolve, reject) {
    console.log(2);
})//1,2複製程式碼

上面的同步任務的情況,當任務中有非同步任務時,在執行棧中非同步任務會進入Event Table並註冊回撥函式放到任務佇列中。當主執行緒中的同步任務執行完後,讀取任務佇列中的非同步任務,放到主執行緒中執行。

如:

console.log(1);
setTimeout(function() {
    console.log(2);
}, 0);
setTimeout(function() {
    console.log(3);
}, 0);
//1,2,3複製程式碼

像這樣任務佇列不斷地取出放到主執行緒中執行,重複以上過程就是事件環(Event Loop)。


上面我們說了,非同步程式碼會進入任務佇列,但是任務佇列中又分為巨集任務佇列和微任務佇列。

巨集任務:

timers(計時器)執行setTimeout以及setInterval回撥

poll(輪詢)執行poll中的i/o佇列檢查定時器是否到時

check(檢查)存放setImmediate回撥

微任務:

process.nextTick

Promise

程式碼放到任務佇列裡時,會區分是放到巨集任務佇列還是微任務佇列,主執行緒任務執行完任務後會首先執行完微任務佇列裡的任務,然後清空微任務佇列,再執行巨集任務佇列裡的任務。

如:

setTimeout(function() {
    console.log(1);
}, 0);
process.nextTick(function() {
    console.log(2);
});
setTimeout(function() {
    console.log(3);
}, 0);
//2,1,3

複製程式碼

當巨集任務裡面有微任務時,會先把巨集任務佇列執行完,切換巨集任務型別之前先執行完微任務,清空微任務佇列

如timer這個佇列,執行巨集任務沒有切換巨集任務型別:

setTimeout(function() {
    console.log(1);
    process.nextTick(function() {
        console.log(2);
    });
}, 0);

setTimeout(function() {
    console.log(3);
    process.nextTick(function() {
        console.log(4);
    });
}, 0);
//1,3,2,4複製程式碼

如timer和poll這個佇列,執行巨集任務有切換巨集任務型別:

let fs = require("fs")
fs.readFile("1.txt",function(){
    console.log(1);
    process.nextTick(function() {
        console.log(2);
    });
})
setTimeout(function() {
    console.log(3);
    process.nextTick(function() {
        console.log(4);
    });
}, 0);
//3,4,1,2複製程式碼

當巨集任務為不同的型別時,巨集任務執行順序為timers -- poll -- check,但是還是會看誰先執行完

如timers在check前面:

setTimeout(function() {
    console.log(1);
}, 1000);
setImmediate(function(){
    console.log(2)
});
//2,1
//因為check執行的時間更短複製程式碼

如timers在check前面,但是timer執行的時間不確定:

setTimeout(function() {
    console.log(1);
}, 0);
setImmediate(function(){
    console.log(2)
});
//2,1或者1,2複製程式碼

巨集任務裡面有巨集任務時,裡面的巨集任務會在當前的巨集任務下面比較

如poll中巢狀timers和check,程式碼執行完poll後會先走poll順序下的check,然後才執行timers而不是上面那樣誰的時間短執行誰:

let fs = require("fs")
fs.readFile("1.txt",function(){
    setTimeout(function() {
        console.log(1);
    }, 0);
    setImmediate(function(){
        console.log(2)
    })
})
//2,1複製程式碼

微任務中由於process.nextTick比promise中的then快所以同一個微任務佇列中process.nextTick先執行

new Promise(function(resolve, reject) {
    resolve()
}).then(function(){
    console.log(2);
})
process.nextTick(function() {
    console.log(1);
})
//1,2複製程式碼

微任務中有巨集任務時,微任務巨集任務執行順序和上面的一樣

如下巨集任務在微任務前執行是因為微任務執行的條件是巨集任務型別切換時觸發,下面的巨集任務都是相同型別的所以沒有觸發微任務

setTimeout(function() {
    console.log(1);
    process.nextTick(function() {
        console.log(2);
    });
}, 0);
process.nextTick(function() {
    console.log(3);
    setTimeout(function() {
        console.log(4);
    }, 0);
})//3,1,4,2複製程式碼

最後總結一下:

巨集任務:timers -- poll -- check 這個任務執行順序和這個任務執行的時間綜合考慮

微任務:process.nextTick --promise

執行微任務的條件是巨集任務型別切換時,或者主執行緒中同步任務執行完微任務佇列還有微任務時



相關文章