js是單線程機制,事件循環是惟一的,可是任務隊列能夠擁有多個html
任務隊列分爲兩種:macroTask(宏任務)與microTask(微任務)。在最新標準中,它們被分別稱爲task與jobshtml5
macroTask大概包括:script(總體代碼)、setTimeout、setInterval、 setImmediate、I/O、UI renderingnode
microTask大概包括: process.nextTick、Promise、 Object.observe(已廢棄)、MutationObserver(html5新特性)promise
setTimeout/Promise等咱們稱之爲任務源。而進入任務隊列的是他們指定的具體執行任務bash
來自不一樣任務源的任務會進入到不一樣的任務隊列。其中setTimeout與setInterval是同源的函數
其中每個任務的執行,不管是macroTask仍是microTask,都是藉助函數調用棧來完成oop
從script(總體代碼)開始第一次循環,以後全局上下文進入函數調用棧。直到調用棧清空(只剩全局),而後執行全部的microTask。當全部可執行的microTask執行完畢以後。循環再次從macroTask開始,找到其中一個任務隊列執行完畢,而後再執行全部的microTask,這樣一直循環下去。post
script(主程序代碼)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI renderingui
setTimeout(function(){console.log(1)},0);
console.log(2)
//2 1
複製代碼
首先執行主線程中的同步任務,當主線程任務執行完以後,再從event loop中讀取任務,所以先輸出2,再輸出1。spa
event loop讀取任務的前後順序,取決於任務隊列(Job queue)中對於不一樣任務讀取規則的限定。
setTimeout(function () {
console.log(4);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
//1 2 3
複製代碼
先輸出1,沒有問題,由於是同步任務在主線程中優先執行,這裏的問題是setTimeout和Promise.then任務的執行優先級是如何定義的?
任務隊列分爲:macroTask(宏任務)與microTask(微任務)。咱們來假設
macroTask隊列包含任務: a1, a2 , a3
microTask隊列包含任務: b1, b2 , b3
執行順序爲,首先執行marcoTask隊列開頭的任務,也就是 a1 任務,執行完畢後,在執行microTask隊列裏的全部任務,也就是依次執行b1, b2 , b3,執行完後清空microTask中的任務,接着執行marcoTask中的第二個任務,依次循環。
setTimeout(function(){
console.log(1)
},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){
console.log(3)
}).then(function(){
console.log(4)
});
process.nextTick(function(){
console.log(5)
});
console.log(6);
//2 6 5 3 4 1
複製代碼
script(主程序代碼)——>process.nextTick——>promise——>setTimeout
更復雜的例子
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){
resolve()
},0)
}).then(function(){
console.log(3)
}).then(function(){
console.log(4)
});
setTimeout(function(){
console.log(1)
},0);
process.nextTick(function(){
console.log(5);
});
console.log(6);
//2 6 5 1 3 4
複製代碼
區別在於Promise的構造中,沒有同步的resolve,所以promise.then在當前的執行隊列中是不存在的,只有promise從pending轉移到resolve,纔會有then方法,而這個resolve是在一個setTimout時間中完成的,所以3,4最後輸出
console.log('1');
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
}).then(function () {
console.log('5')
});
})
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8')
})
setTimeout(function () {
console.log('9');
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log('10')
}).then(function () {
console.log('11')
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log('12')
})
})
複製代碼
一、第一輪事件循環流程分析以下:
宏任務Event Queue | 微任務Event Queue |
---|---|
setTimeout1 | then1 |
setTimeout2 |
第一輪事件循環宏任務結束時各Event Queue的狀況如上表,此時已經輸出了1和7。清空微任務,執行then1,輸出8。第一輪事件循環正式結束,這一輪的結果是輸出1,7,8。
二、第二輪事件循環流分析:
從setTimeout1宏任務開始
宏任務Event Queue | 微任務Event Queue |
---|---|
setTimeout2 | then2 |
第二輪事件循環宏任務執行結束,執行微任務then2,輸出4,then被分發到微任務Event Queue中,記爲then3
宏任務Event Queue | 微任務Event Queue |
---|---|
setTimeout2 | then3 |
執行微任務then3,輸出5。第二輪事件循環正式結束,這一輪的結果是輸出2,3,4,5。
三、第三輪事件循環流分析:
從setTimeout2宏任務開始
宏任務Event Queue | 微任務Event Queue |
---|---|
then4 | |
then5 |
第二輪事件循環宏任務執行結束,執行微任務then4,輸出10,then被分發到微任務Event Queue中,記爲then6。執行微任務then5,輸出12
宏任務Event Queue | 微任務Event Queue |
---|---|
then6 |
執行微任務then6,輸出11。第三輪事件循環正式結束,這一輪的結果是輸出9,10,12,11。
整段代碼,共進行了三次事件循環,完整的輸出爲1,7,8,2,3,4,5,9,10,12,11。(node環境執行結果會有些不同)
參考文章:
從promise、process.nextTick、setTimeout出發,談談Event Loop中的Job queue