搞明白這段代碼的輸出順序,Js異步編程就懂了一半

執行下面這段代碼,若是和你想的輸出順序一致,那麼你可能已經掌握了Js的異步編程,本文對你來講或許沒太大幫助。什麼,輸出順序和你想的不同?請往下看:編程

async function log(x){
  console.log(x)
  setTimeout(()=>{ console.log('H')},0);
  new Promise((resolve,reject)=>{
    console.log("G");
    resolve();
  }).then(()=>{
    console.log('J')
  })
  console.log('I');
}


new Promise((resolve,reject)=>{
  console.log('A');
  resolve();
}).then(()=>{
  console.log('B')
})

console.log('C');

setTimeout(function(){
  console.log('D')
},0)

log('E')
console.log('F');
debugger;
複製代碼

執行代碼,開始第一輪tick,首先遇到Promisebash

  • Promise構造函數內部屬於宏任務,當即執行,輸出A,而後resolve().then()的回調會加入此輪tick尾部
  • 輸出C
  • 遇到定時器,0秒後將回調函數內的操做加入下一輪tick
  • 執行async函數log()
    • 輸出E
    • 遇到定時器,0秒後將回調函數內的操做加入下一輪tick
    • 遇到Promise,輸出G,.then()的回調會加入此輪tick尾部
    • 輸出I
  • 輸出F
  • 第一輪tick結束,執行tick尾部的微任務
    • 輸出B
    • 輸出J
  • 開始第二輪tick
  • 輸出D
  • 輸出H
  • 事件隊列爲空,運行完畢

另外,若是async函數內部出現await,那麼執行狀況是怎樣的呢?咱們來在async函數中await一個Promise,代碼以下:異步

async function log(x){
  console.log(x)
  setTimeout(()=>{ console.log('H')},0);
  new Promise((resolve,reject)=>{
    console.log("G");
    resolve();
  }).then(()=>{
    console.log('J')
  })
  
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  await new Promise((resolve,reject)=>{
    console.log('await 1');
    resolve()
  }).then(()=>{
    console.log('await 2');
  })
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  console.log('I');
}

複製代碼

你若是執行下代碼,會發如今插入await以後,執行順序發生了很大變化:async

  • 第一輪tick
  • 執行async函數log()以前的順序沒有改變。輸出A、輸出C,且B加入此輪tick尾部,D加入下一輪tick
  • 執行async函數log()
    • 輸出E
    • 遇到定時器,0秒後將回調函數內的操做加入下一輪tick
    • 遇到Promise,輸出G,.then()的回調會加入此輪tick尾部
    • 此時,就到了await的手中,嘿嘿,不過await的是Promise構造器,輸出await 1

await會將當前async函數的執行權交出,等待Promise的狀態改變後,再繼續執行,而且await返回的是一個Promise。因此我理解爲await以後的代碼加入了此輪tick尾部(執行驗證發現,確實是這樣)。異步編程

  • 輸出F
  • 第一輪tick結束,輸出tick
    • 輸出B
    • 輸出J
    • 輸出await 2
    • 輸出I
  • 第二輪tick開始
    • 輸出D
    • 輸出H
  • 事件隊列爲空,運行完畢
相關文章
相關標籤/搜索