參考文章:html
經過事件循環(event loop)git
js 是單線程,可將任務分爲兩類:github
先看一段代碼:segmentfault
console.log('script start'); //同步任務
setTimeout(function() { //異步任務,同步任務完成後,0秒後執行
console.log('setTimeout');
}, 0);
//Promise中的 .then()裏的函數是異步任務
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end'); //同步任務
複製代碼
答案是:script start, script end, promise1, promise2, setTimeoutpromise
爲何會這樣的順序? 如圖: 異步
解讀:async
首先,微任務和宏任務都是異步任務,它們都屬於一個隊列,主要區別在於他們的執行順序 函數
綜合分類方式:oop
栗子1:post
//同步任務,放入主線程裏
console.log(1);
//異步任務,被放入 event table,0秒後被推入 event queue 裏
setTimeout(function () {
console.log(2)
}, 0);
//同步任務,放入主線程裏
console.log(3);
複製代碼
因此打印順序: 1,3,2
栗子2:
setTimeout(function(){
console.log('定時器開始')
});
new Promise(function(resolve){
console.log('立刻執行for循環');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('執行then函數')
});
console.log('代碼執行結束');
複製代碼
macrotasks=['setTImeout']
microtasks=['then']
microtasks=['then']
, 輸出"執行then函數"macrotasks=['setTImeout']
, 輸出"定時器開始"栗子3:
setTimeout(() => {
cosnole.log('A');
},0)
var obj = {
func: function() {
setTimeout(function () {
console.log('B')
}, 0);
return new Promise(function () {
console.log('C');
resolve();
})
}
}
obj.func().then(function() {
console.log('D')
})
console.log('E')
// C E D A B
複製代碼
macrotasks=['A']
;macrotasks=['A','B']
;microtasks=['C']
栗子4:
console.log('1');
//命名setTimeout1
setTimeout(function() {
console.log('2');
process.nextTick(function() { //命名process2
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() { //命名then2
console.log('5')
})
})
//命名process1
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() { //命名 then1
console.log('8')
})
//命名setTimeout2
setTimeout(function() {
console.log('9');
process.nextTick(function() { //命名process3
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() { //命名 then3
console.log('12')
})
})
複製代碼
macrotasks = ['setTimeout1']
,microtasks=['process1']
microtasks=['process1', 'then1']
macrotasks = ['setTimeout1', 'setTimeout2']
,microtasks=['process1', 'then1']
,因此輸出’六、8‘。任務執行完成後,微任務的event queue清空,即microtasks = []
。microtasks=['process2', 'then2']
microtasks=['process2', 'then2']
,因此輸出’三、5‘,任務執行完後,微任務的event queue清空,即microtasks = []
。macrotasks = ['setTimeout2']
,同理輸出’九、11‘,此時的microtasks=['process3, 'then3']
。microtasks=['process3, 'then3']
,因此輸出’十、12‘(...以爲本身好羅嗦)
栗子5:
async function async1 () {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2 () {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
//輸出順序:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
複製代碼
實際上 await 是一個讓出線程的標誌,await 後面的表達式會先執行一遍,將 await 後面的代碼加入到 microtask 中,而後就會跳出整個 async 函數來執行後面的代碼
async function async1 () {
console.log('async1 start');
await async2();
console.log('async1 end');
}
//等價於
function async1 () {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
複製代碼
macrotasks = [setTimeout]
;microtasks = ['async1 end']
;microtasks = ['async1 end', 'promise2']