JavaScript 中的 async/await 是什麼?和 promise 有什麼差別?
2024年3月30日
在之前的文章中
我們分別討論過事件循環 (Event Loop)、異步(非同步) 和 Promise 的概念,而在這篇文章中,我們將深入探討 async/await 是什麼?以及它與 Promise 的差異。
async/await 是什麼?
在 JavaScript 中,async/await 是一種讓異步(非同步)操作更容易理解和管理的語法。它建立在 Promise 的基礎上,但提供了更簡潔、更直觀的方式來處理異步操作。
以下我們先把 async 和 await 拆開來看:
async 語法
使用 async 關鍵字聲明的函式為異步函式,異步函式會返回一個 Promise 物件,而非直接返回函式執行的結果。讓我們透過範例來了解:
- 下方普通函式
f1()直接返回字串"Hello! ExplainThis!"
// 普通函式
function f1() {
return "Hello! ExplainThis!";
}
f1(); // 輸出: "Hello! ExplainThis!"
- 下方程式碼中,
async function f2() {...}定義了一個名為f2的異步函式,該函式返回字串"Hello! ExplainThis!",並將其封裝在一個 Promise 物件中。
// 異步函式
async function f2() {
return "Hello! ExplainThis!";
}
f2(); // 輸出: Promise {<fulfilled>: 'Hello! ExplainThis!'}
上方程式碼寫法跟下方寫法其實是相同的,因為使用 async 時,會自動將回傳值包裝在一個 Promise 物件當中。
// 異步函式
function f3() {
return Promise.resolve("Hello! ExplainThis!");
}
f3(); // 輸出: Promise {<fulfilled>: 'Hello! ExplainThis!'}
由於 async 函式總是返回一個 Promise 對象,如果要獲取該 Promise 的解析值,可以使用 .then() 方法:
async function f2() {
return "Hello! ExplainThis!";
}
f2().then((result) => {
console.log(result); // "Hello! ExplainThis!"
});
await 語法
await 是一個運算子,用於等待一個 Promise 完成或拒絕。它通常與 async 函式一起使用,因為只有在 async 函式內部或模組的頂層,才能使用 await。
當使用 await 時,程式會暫停執行該 async 函式,直到 await 等待的 Promise 完成並回傳結果後,才會繼續往下執行。讓我們透過下方範例來了解:
async function getData() {
// await 等待 fetch 這個非同步函式返回一個 Promise 並解析它
const res = await fetch("https://example.com/data");
// await 等待上一步的 Promise 解析後,再解析它的 JSON 資料
const data = await res.json();
// 前面兩步都完成後,才會執行這一行並印出資料
console.log(data);
}
getData();
使用 await 要注意的幾點
- 在非
async函式中使用await會報 SyntaxError 的錯誤
function f() {
let promise = Promise.resolve("Hello! ExplainThis!");
let result = await promise;
}
// Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
- 頂層
await(Top levelawait)
頂層 await 是一個在 JavaScript 中引入的新語法功能,它允許在模組的最頂層使用 await 關鍵字。在 ES 模組中,原本只有 async 函式內部才能使用 await。但是使用頂層 await 後,就可以直接在模組頂層使用 await 了。
例如,如果想在某個模組中獲取遠端資源,以前需要這樣寫:
import getData from "./getData.js";
let data;
getData().then((result) => {
data = result;
// ...使用data
});
有了頂層 await (Top level await),你就可以這樣更直接地寫:
const data = await getData();
// ...使用data
如何使用 async/await?
使用 async/await 可以將異步程式碼寫成類似同步的形式,使其更易讀、且更易維護。讓我們先看一個使用 Promise 寫的 getData 函式範例:
我們先來看用 Promise 來寫一個 getData 函式的例子:
function getData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((res) => res.json())
.then((data) => resolve(data))
.catch((error) => reject(error));
});
}
getData("https://example.com/data")
.then((data) => console.log(data))
.catch((error) => console.error(error));
在這個例子中,getData 函式使用 Promise 來處理異步操作。我們需要使用 .then() 和 .catch() 方法來獲取結果或錯誤。
現在,我們使用 async/await 來重寫 getData 函式:
async function getData(url) {
try {
const res = await fetch(url);
const data = await res.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
getData("https://example.com/data");
在這個例子中:
- 使用
async關鍵字定義一個異步函式,該函式會返回一個 Promise 對象。 - 在異步函式中,使用
await等待 Promise 的完成,並直接返回結果。 - 使用
try...catch捕獲錯誤,使得錯誤處理更加方便和直觀。
可以看到,使用 async/await 後,程式碼變得更加清晰和易於理解。
async/await 與 Promise 的差別?
async/await 和 Promise 都是用於處理異步操作的方式,但它們有以下一些差異:
- 語法:
async/await提供了更簡潔、更直觀的語法,使得異步程式碼更易讀和維護。Promise 則需要使用then和catch方法來處理結果和錯誤,語法上較為冗長。 - 錯誤處理: 在
async/await中,可以直接使用try...catch來捕獲錯誤,而在 Promise 中需要使用catch方法。 - 程式碼流程:
async/await可以使異步程式碼看起來更像同步程式碼,更容易閱讀和理解。Promise 的程式碼流程則較為不連貫。