1. 程式人生 > >promise、async和await之執行順序的那點事

promise、async和await之執行順序的那點事

故事要從一道今日頭條的筆試題說起~ 題目來源:半年工作經驗今日頭條和美團面試題面經分享!!!!!(https://juejin.im/post/5b03e79951882542891913e8)

  1. async function async1(){

  2.    console.log('async1 start')

  3.    await async2()

  4.    console.log('async1 end')

  5. }

  6. async function async2(){

  7.    console.log('async2')

  8. }

  9. console.log('script start')

  10. setTimeout(function(){

  11.    console

    .log('setTimeout')

  12. },0)

  13. async1();

  14. newPromise(function(resolve){

  15.    console.log('promise1')

  16.    resolve();

  17. }).then(function(){

  18.    console.log('promise2')

  19. })

  20. console.log('script end')

求列印結果是什麼?

相信是個前端都知道啦,這道題目考的就是 js 裡面的事件迴圈和回撥佇列咯~ 今天題主假設看客都已經瞭解了 setTimeout 是巨集任務會在最後執行的前提(因為它不是今天要討論的重點),我們主要來講講 promiseasync 和 await

 之間的關係。

先上正確答案:

  1. script start

  2. async1 start

  3. async2

  4. promise1

  5. script end

  6. promise2

  7. async1 end

  8. setTimeout

事實上,沒有在控制檯執行列印之前,我覺得它應該是這樣輸出的:

  1. script start

  2. async1 start

  3. async2

  4. async1 end

  5. promise1

  6. script end

  7. promise2

  8. setTimeout

為什麼這樣認為呢?因為我們(粗淺地)知道 await 之後的語句會等 await 表示式中的函式執行完得到結果後,才會繼續執行。

MDN 是這樣描述 await 的:

async 函式中可能會有 await 表示式,這會使 async 函式暫停執行,等待表示式中的 Promise 解析完成後繼續執行 async 函式並返回解決結果。

會認為輸出結果是以上的樣子,是因為沒有真正理解這句話的含義。

阮一峰老師的解釋我覺得更容易理解:

async 函式返回一個 Promise 物件,當函式執行的時候,一旦遇到 await 就會先返回,等到觸發的非同步操作完成,再接著執行函式體內後面的語句。

對啦就是這樣,MDN 描述的暫停執行,實際上是讓出了執行緒(跳出 async 函式體)然後繼續執行後面的指令碼的。這樣一來我們就明白了,所以我們再看看上面那道題,按照這樣描述那麼他的輸出結果就應該是:

  1. script start

  2. async1 start

  3. async2

  4. promise1

  5. script end

  6. async1 end

  7. promise2

  8. setTimeout

好像哪裡不太對?對比控制檯輸出的正確結果,咦~有兩句輸出是不一樣的呀!!

  1. async1 end

  2. promise2

為什麼會這樣呢?這也是這道題目最難理解的一個地方。要搞明白這個事情,我們需要先來回顧一些概念:

async

async function 宣告將定義一個返回 AsyncFunction 物件的非同步函式。

當呼叫一個 async 函式時,會返回一個 Promise 物件。當這個 async 函式返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函式丟擲異常時,Promise 的 reject 方法也會傳遞這個異常值。

所以你現在知道咯,使用 async 定義的函式,當它被呼叫時,它返回的其實是一個 Promise 物件。 我們再來看看 await 表示式執行會返回什麼值。

await

語法:[return_value] = await expression;

表示式(express):一個 Promise 物件或者任何要等待的值。

返回值(return_value):返回 Promise 物件的處理結果。如果等待的不是 Promise 物件,則返回該值本身。

所以,當 await 操作符後面的表示式是一個 Promise 的時候,它的返回值,實際上就是 Promise 的回撥函式 resolve 的引數。

明白了這兩個事情後,我還要再囉嗦兩句。我們都知道 Promise 是一個立即執行函式,但是他的成功(或失敗:reject)的回撥函式 resolve 卻是一個非同步執行的回撥。當執行到 resolve() 時,這個任務會被放入到回撥佇列中,等待呼叫棧有空閒時事件迴圈再來取走它。

終於進入正文:解題

好了鋪墊完這些概念,我們回過頭看上面那道題目困惑的那兩句關鍵的地方(建議一邊對著題目一邊看解析我怕我講的太快你跟不上啊哈哈