天天看點

js的事件循環機制,同步和異步,以及宏任務與微任務的執行順序

前置知識點(重要):

1.什麼是事件循環:js是單線程語言,同個時間執行一件事(同步),但是他可以有一個異步隊列,遇到異步操作(比如說ajax這種阻塞時間很久的事情)把它們先放入異步隊列,并且繼續往下執行,當同步隊列執行完了,他就會去異步隊列裡面找剛才存放起來的事件,然後按順序執行他們。

2.異步隊列(異步任務)又包含宏任務和微任務,微任務先與宏任務執行

宏任務有:

# 浏覽器 Node
setTimeout
setInterval
setImmediate x
requestAnimationFrame x

微任務有:

# 浏覽器 Node
process.nextTick x
MutationObserver x
Promise.then catch finally

3.同為微任務的Promise與process.nextTick(callback),先執行後者(process.nextTick(callback)當下一輪事件開始循環的時候第一時間執行他的callback)

下圖為他們之間的關系圖:

js的事件循環機制,同步和異步,以及宏任務與微任務的執行順序

通過一個例子來解釋執行整個事件循環,與同步異步,宏任務微任務執行順序

console.log('1');

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

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 
           

1.第一輪事件循環流程分析如下:(第一輪描述詳細一點,接下去都差不多)

– 進入主線程,遇到console.log,輸出1。

遇到setTimeout,其回調函數被分發到宏任務Event Queue中。我們暫且記為setTimeout1。

– 遇到process.nextTick(),其回調函數被分發到微任務Event Queue中。我們記為process1。

– 遇到Promise,new Promise(是同步,then才是異步)直接執行,輸出7。then(Promise中then是異步)被分發到微任務Event Queue中。我們記為then1。

– 又遇到了setTimeout,其回調函數被分發到宏任務Event Queue中,我們記為setTimeout2。

宏任務Event Queue 微任務Event Queue
setTimeout1 process1
setTimeout2 then1

上表是第一輪事件循環宏任務結束時各Event Queue的情況,此時已經輸出了1和7。

我們發現了process1和then1兩個微任務。

執行process1,輸出6。

執行then1,輸出8。

好了,第一輪事件循環正式結束,這一輪的結果是輸出1,7,6,8。

2.第二輪時間循環從setTimeout1宏任務開始:

首先輸出2。接下來遇到了process.nextTick(),同樣将其分發到微任務Event Queue中,記為process2。

new Promise立即執行輸出4,then也分發到微任務Event Queue中,記為then2

宏任務Event Queue 微任務Event Queue
已被取出 process2
setTimeout2 then2

如表所示:這一輪取出setTimeout1,分析發現兩個微任務,則先執行這兩個微任務。

3.第三輪宏任務setTimeout2開始執行

宏任務Event Queue 微任務Event Queue
已被取出 process3
已被取出 then3

如表所示:這一輪取出setTimeout2,分析發現兩個微任務,則先執行這兩個微任務process3和then3。

輸出10。

輸出12。

第三輪事件循環結束,第三輪輸出9,11,10,12。

整段代碼,共進行了三次事件循環,完整的輸出為1,7,6,8,2,4,3,5,9,11,10,12。(請注意,node環境下的事件監聽依賴libuv與前端環境不完全相同,輸出順序可能會有誤差)

圖解js執行機制–事件循環

js的事件循環機制,同步和異步,以及宏任務與微任務的執行順序

首先,整體的script(作為第一個宏任務)開始執行的時候,會把所有代碼分為同步任務、異步任務兩部分

同步任務會直接進入主線程依次執行

異步任務會再分為宏任務和微任務

宏任務進入到Event Table中,并在裡面注冊回調函數,每當指定的事件完成時,Event Table會将這個函數移到Event Queue中

微任務也會進入到另一個Event Table中,并在裡面注冊回調函數,每當指定的事件完成時,Event Table會将這個函數移到Event Queue中

當主線程内的任務執行完畢,主線程為空時,會檢查微任務的Event Queue,如果有任務,就全部執行,如果沒有就執行下一個宏任務

上述過程會不斷重複,這就是Event Loop,比較完整的事件循環

繼續閱讀