詳解promise、async和await的執行順序


1、題目和答案

一道題題目:下面這段promise、async和await代碼,請問控制台打印的順序?

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')

上述,在Chrome 66node v10中,正確輸出是:

script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout

2、知識點

顯然,這考察的是js中的事件循環和回調隊列。注意以下幾點:

  • Promise優先於setTimeout宏任務。所以,setTimeout回調會在最后執行。
  • Promise一旦被定義,就會立即執行。
  • Promiserejectresolve是異步執行的回調。所以,resolve()會被放到回調隊列中,在主函數執行完和setTimeout前調用。
  • await執行完后,會讓出線程。async標記的函數會返回一個Promise對象

3、 難點

最令人困惑的,就是async1 endpromise2之后輸出

在函數async1中,執行promise由於async2async標記的函數,所以默認返回promise對象)會發現resolve(),然后放入回調隊列。

接着執行下方的new Promise中的resolve()輸出promise2,再回來輸出async1 end

其中,async1函數可以寫成以下方式(便於理解):

async function async1(){
  console.log('async1 start')
  async2().then( _ => {
    console.log( 'async1 end ')
  })
}

3、流程

  1. console.log('script start')輸出:script start
  2. setTimeout被放在最后調用
  3. 執行async1函數,輸出async1 start。然后,進入async2函數,輸出async2,並返回Promise對象。回到async1,由於await,讓出線程,async2函數返回的Promise放在回調隊列
  4. 新new了一個Promise對象,輸出promise1。其中的resolve()被放在回調隊列。
  5. console.log('script end')輸出:script end
  6. 執行回調隊列中,async1返回的Promise對象,對象產生的resolve被放入對調隊列。這里不輸出任何值。
  7. 執行回調隊列中,下方Promise顯式聲明的resolve,輸出promise2
  8. 執行回調隊列中,由於async1函數返回的promise對象的resolve,輸出async1 end
  9. 執行回調隊列中,最后的setTimeout,輸出setTimeout
  10. finish

4、參考

  1. promise、async和await之執行順序的那點事
  2. 半年工作經驗今日頭條和美團面試題面經分享
  3. 關於node和chrome運行結果不一樣的詳解

歡迎技術交流,引用請注明出處。
個人網站:godbmw.com
Github:godbmw


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM