console.log('同步-0.1')
Promise.resolve().then(() => {
console.log('P-1.1')
})
setTimeout(() => {
console.log('S-1.1')
});
Promise.resolve().then(() => {
console.log('P-1.2')
})
setTimeout(() => {
console.log('S-1.2')
});
console.log('同步-0.2')
執行結果如下。。。問題暴露出來了:
我順便說下,會先把所有的同步代碼執行後,纔會開始異步,所以第二條纔會輸出:同步-0.2
setTimeout不會馬上執行,是會放到隊列事件裏面
同步-0.1
同步-0.2
P-1.1
P-1.2
S-1.1
S-1.2
拋出問題:爲什麼,在同級情況下,是Promise
執行完了setTimeout
纔會執行?
JavaScript的任務分爲微任務(Microtasks)和宏任務(task);
- 宏任務是主流,當js開始被執行的時候,就是開啓一個宏任務,在宏任務中執行一條一條的指令;
- 宏任務可以同時有多個,但會按順序一個一個執行;
- 每一個宏任務,後面都可以跟一個微任務隊列,如果微任務隊列中有指令或方法,那麼就會執行;如果沒有,則開始執行下一個宏任務,直到所有的宏任務執行完爲止,微任務相當於宏任務的小尾巴;
- 爲什麼有了宏任務,還會有微任務存在?因爲宏任務太佔用性能,當需要一些較早就準備好的方法,排在最後才執行的時候,又不想新增一個宏任務,那麼就可以把這些方法,一個一個的放在微任務隊列裏面,在這個宏任務中的代碼執行完後,就會執行微任務隊列。
而Promise
是微任務,setTimeout
是宏任務。
所以上面的代碼中,代碼執行時會是如下場景:
開始執行當前宏任務代碼!遇到了
Promise
?好嘞,把它裏面的異步代碼,放在當前這個宏任務後面微任務裏面,然後繼續執行咱的;咦,有個
setTimeout
?是個宏任務,那在當前這個宏任務後面,創建第二個宏任務,然後把這個setTimeout
裏面的代碼塞進去,咱繼續執行;咦,又一個
Promise
?把他塞進後面的微任務裏。。。什麼?已經有代碼了?那有啥關係,繼續往裏塞,放在已有代碼的後面就行,咱繼續執行;天啊,又來一個
setTimeout
,現在後面已經有第二個宏任務了對吧?那就創建第三個宏任務吧,後面再遇到的話,繼續創建;報告!代碼執行到底了,當前這個宏任務執行完畢!
行,看一下咱的小尾巴---咱的微任務裏面有代碼嗎?有的話直接執行;報告,微任務裏面,那兩個
Promise
的異步代碼執行完了!
乾的漂亮。。。對了,剛剛微任務裏面,有沒有新的Promise
微任務?有的話,繼續在現在這個微任務後面放!對對,只看執行到的代碼,有多少放多少,一會兒直接就執行了。。。如果遇到了setTimeout
知道該怎麼做吧?繼續開宏任務!報告,微任務全部執行完畢!
好!開始執行下一個宏任務!
所以,現在如果執行下面的代碼,結果也顯而易見吧:
console.log('同步-0.1')
Promise.resolve().then(() => {
console.log('P-1.1')
Promise.resolve().then(() => { // 新加行
console.log('P-2.1') // 新加行
Promise.resolve().then(() => { // 新加行
console.log('P-3.1') // 新加行
}) // 新加行
}) // 新加行
})
setTimeout(() => {
console.log('S-1.1')
});
Promise.resolve().then(() => {
console.log('P-1.2')
})
setTimeout(() => {
console.log('S-1.2')
});
console.log('同步-0.2')
答案:
同步-0.1
同步-0.2
P-1.1
P-1.2
P-2.1
P-3.1
S-1.1
S-1.2
結論:無論Promise
套用多少層,都會在下一個setTimeout
之前執行完Promise
。