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