JS之路 Day17 - async/await(Promise語法糖)
前言
async/await
的方式提供了promise
一種新的寫法,至於有沒有更方便及更高的可讀性,我覺得使用了async/await
是更容易進行編寫的,至少可以不用一直.then
下去,使用了async/await
之後,就跟.then
說再見。
今天會介紹的順序:
- 這兩個好用的關鍵字分別代表的意思。
- async/await 所達成的「同步」效果。
- 錯誤處理的應對措施。
async
這是一個加在function
前面的關鍵字,沒有加就是普通的function
,加了之後就會變成async function
就會代表著現在這個function
是一個非同步的function
。
1 | // 普通的function |
而會說加上async
後function
會變成非同步的原因主要是因為async
會讓function
在回傳的時候會返回一個promise
,所以因為是promise
,所以也可以使用.then
來取出結果。
1 | async function add(num) { |
所以跟這樣也是會相同的,不加上async
的function
:
1 | function add(num) { |
加了async
的async function
除了可以讓整體變成非同步的function
之外,還可以使用一種名為await
的招式。
await
這些從它的單字意思其實就能略知一二,await
是等待,在這裡當關鍵字的就是就是讓這個async function
進行等待。
這邊很重要的一點就是,一定要是async function
,沒有的話就沒辦法使用,可是說是有async
才有await
(但沒有await
也可以用async
)。
1 | function add() { |
沒有async
的狀態下直接使用await
就會報錯給你看。
等待主要是讓Promise
的運行先暫停一下,直到接受了回傳值才會繼續運行,而await
這種藉由等待,暫停了非同步的執行的方式,是讓使用了async/await
的function
看起來很像同步的關鍵。
明明是非同步,為什麼會看起來很像同步呢? 看過來。
async/await
的「同步」
1 | const promise = new Promise(function (resolve) { |
在講這段程式之前,先來談談順序的問題。
一般來說,程式碼看下來的流程,會先去執行同步的程式,因為不會有需要等待排隊的問題,而非同步的因為沒辦法馬上完成,所以要等待後才能執行,等待的時間不知道,但能知道的是一定同步的東西先跑完。
程式碼的寫法順序:
1 | 同步1 |
實際上的執行順序:
1 | 同步1 |
基本上有兩個概念在執行順序這一塊:
- 會由上而下執行
- 同步先執行,才是非同步執行
回到程式碼,照著剛剛的概念來看,應該會是先跑出來在 await 之前
之後是在 await 之後
,而經由了setTimeout
而造成需要等待幾秒才會執行的promise
非同步理論上應該是最後才會跑出來,但是如果你把上面那段程式碼拿去執行,你會發現順序是:
1 | 在 await 之前 |
怎麼跟上面講的不一樣呢?這其實就是async/await
的神奇之處,使用了await
就會等待,就算它本身是一個非同步的運行,在整體的async function
之中,也會逐行的執行,不會有某一行被抓去排隊(事件佇列)而執行跳到下一行的狀況,看起來就可以達成是非同步的,但我用同步的方式去跑。
錯誤處理
await
後面接的promise
會直接回傳結果,而結果有兩種可能性。
- 成功 => resolve
- 失敗 => reject
在原本使用promise
的時候,會使用.then
跟.catch
去獲取這兩種結果,但是async/await
不用。
我研究之後發現在async/await
時會使用try..catch
的方式來處理錯誤的結果。
try..catch
它會分成兩個區塊,一個 try 一個 catch,主要就是正常是 try 區塊,在 try 區塊碰到錯誤,直接跳到 catch 區塊。
1 | try { |
舉例時間:
1 | try { |
這段 code 的結果會印出我錯了,如下:
為什麼會發生這樣的結果?這是因為如果 try 區塊裡面的程式碼假如沒有任何的錯誤,就會直接忽略掉catch
區塊裡面的程式碼,反之有錯的話,就會以catch
裡面的程式碼為主,而錯誤的try
就會中斷執行,所以在這個範例來說,因為try
寫錯了,所以會以try
裡面的程式碼為主。
另外如果catch
區塊如果接受了一個參數,就可以直接利用這個參數獲取錯誤資料,一般都會是直接在後台輸出資料,這邊來試試看:
1 | try { |
這段 code 的結果會印出錯誤資訊,如下:
async/await
遇錯時
使用剛剛講的方式,先把全部的async function
都用try..catch
包起來,然後錯誤的話自然就會跑到catch區塊
自動去抓取失敗的結果。
1 | function promise(a) { |
最後結果印出:
1 | 在 await 之前 |
第一行的在 await 之前
還是會出現,只是經過了下方await
的promise
,因為裡面帶入的參數是負數,所以是false
,結果會跑去else
的reject("錯誤發生")
,而最終會進到catch
區塊直到結束,所以try
區塊後面的在 await 之後
就不會看到。
要注意一件事情,try
區塊可以因為錯誤而進到catch
區塊,但沒辦法反向,也就是說無法在失敗之後再度執行原本程式。
總結
async function
的奇妙之處:
- 內部可以使用名為
await
的招式。 - 內部最終總是會回傳一個
promise
。 - 整體會變成非同步的狀況。
await
這個招式使用之後的兩種下場:
- 強制
promise
等待直到完成。 - 有錯誤的話,拋出
reject
的錯誤結果。 - 沒錯誤就直接回傳
resolve
的成功結果。
什麼時候可以用?
基本上當你想要使用promise
的.then
時,都可以把它改成async/await
的方式,就不用一直.then
,處理非同步的程式時又多了一種寫法,看完這這篇後,大家也多嘗試用看看async/await
來寫 code 吧。
reference
[1] MDN - async function
[2] MDN - await
[3] w3schools - JavaScript Async