setTimeout和Promise進行混合操作的執行順序

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。 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章