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