非同步概念的最後一小塊拼圖,要來學習怎麼使用 async/await
async/await 是 ECMAScript 2017 引入的語法糖,對於遇到非同步腦袋就打結的我,async/await 簡直就是救星~
async/await 像個連體嬰一樣總是形影不離,不過它們長得還是不一樣嘛,所以還是先分開認識一下
如果在一般函式前加上 async 關鍵字,代表我們宣告了一個非同步函式
先來看看一般函式,和在同一個函式前加上 async 的區別
可以觀察到只要在函式前加上 async,就保證這個函式會回傳一個 Promise
,即使函式的回傳值只是單純的字串或數字,也會自動用 Promise 包裹起來
await 會放在非同步函式前,他的作用是等待非同步函式執行完成並回傳結果,不需要像 Promise 得使用.then() 取得 resolve 的值。
//非同步函式,等待兩秒後回傳顧客點的飲料
function makeDrinks(drinks) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(drinks);
}, 2000);
});
}
async function sellDrinks() {
const firstOrder = await makeDrinks("black tea");
console.log(firstOrder);
}
sellDrinks(); // black tea
// 在一般函式內使用 await 會報錯
function sellDrinks() {
const firstOrder = await makeDrinks("black tea");
console.log(firstOrder);
}
sellDrinks();
//SyntaxError: await is only valid in async function
對關鍵字有基礎認識後,用一個簡單的範例理解 async/await 的執行步驟
async function foo() {
const result = await new Promise((resolve) => setTimeout(() => resolve('1'), 1000))
console.log(result)
}
foo();
// 一秒過後,印出1
理解執行的順序後,應該就不難理解下述的例子為什麼執行時間會是4秒
function makeDrinks(drinks) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(drinks);
}, 2000);
});
}
async function sellDrinks() {
console.time();
const firstOrder = await makeDrinks("black tea");
const secondOrder = await makeDrinks("bubble tea");
console.log(`Here is your ${firstOrder}`);
console.log(`Here is your ${secondOrder}`);
console.timeEnd();
}
// Here is your black tea
// Here is your bubble tea
// default: 4.023s
下面兩個例子的執行時間大約都是兩秒左右,但 MDN 並不建議第一種做法,如果希望非同步函式同時執行的話,建議使用Promise.all()
或Promise.allSettled()
// 寫法1
async function sellDrinks() {
console.time();
const order = makeDrinks("black tea");
const extraOrder = makeDrinks("bubble tea");
const totalOrder = (await order) + "," + (await extraOrder);
console.log(`Here is your ${totalOrder}`);
console.timeEnd();
}
sellDrinks();
// Here is your black tea,bubble tea
// default: 2.029s
//寫法2
async function sellDrinks() {
console.time();
const totalOrder = await Promise.all([
makeDrinks("black tea"),
makeDrinks("bubble tea"),
]);
console.log(`Here is your ${totalOrder[0]}`);
console.log(`Here is your ${totalOrder[1]}`);
console.timeEnd();
}
sellDrinks();
// Here is your black tea
// Here is your bubble tea
// default: 2.031s
使用了 async/await 後,不僅寫非同步感覺像在寫同步,在錯誤處理方面也可以用熟悉的try...catch語法。
之前看著學姊用 async/await 覺得好羨慕啊,寫起來輕鬆又好讀,但自己要寫卻不知從何寫起,現在回頭想想大概是因為當時尚未理解非同步的整個脈絡。
慢慢從 callback 循序理解 Promise 的使用,再嘗試將 Promise 替換成 async/await 後,對於 Promise 又再理解了一些些。
參考資料:
MDN
JAVASCRIPT.INFO
Asynchronous 非同步進化順序 - Async/Await