September 21, 2023

Viiisit [JavaScript] - Promise & async/await!

#javascript

繼上篇提及到回呼函式(Callback Function),今天來講 Promise 與 async/await!

Promise 是什麼?

Promise 是 JavaScript 中用於處理非同步操作的一種概念和對象。
主要目的是更好地處理非同步操作的 Callback Hell (回調地獄) 問題,使代碼更具可讀性和可維護性。

Promise 的三種狀態

Promise 的主要用途

Promise 的重要方法

.then().catch() 使用情境

想像你正在訂外賣,並希望知道當外賣送達時要做些什麼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function orderFood() {
console.log("開始訂外賣...");

const deliveryTime = Math.random() * 10000; // 模擬外賣送達時間(0 到 10 秒)

// 使用 Promise 模擬外賣送達
const deliveryPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.2; // 模擬外賣送達成功或失敗
if (success) {
resolve("外賣已送達!");
} else {
reject("對不起,配送出了問題。");
}
}, deliveryTime);
});

// 使用 .then() 處理外賣送達成功情況
deliveryPromise.then(successMessage => {
console.log(successMessage);
console.log("確保你有筷子、餐盤和餐巾紙。");
});

// 使用 .catch() 處理外賣送達失敗情況
deliveryPromise.catch(errorMessage => {
console.error(errorMessage);
console.log("不要擔心,你可以聯繫外賣店解決問題。");
});

console.log("訂單已下單,等待外賣送達...");
}

// 訂外賣
orderFood();

我們使用 .then() 方法處理成功情況,當外賣成功送達時,我們顯示成功消息並給予一些提示。
當外賣送達失敗時,我們使用 .catch() 方法處理錯誤情況,並提供相應的錯誤消息和建議。

.finally() 使用情境

讓我們通過一個簡單的實際例子來看看 .finally() 的使用情境。

假設你正在開發一個文件上傳功能,用戶可以上傳文件到服務器。
在每次文件上傳之前,你希望顯示一個加載器,無論上傳成功還是失敗,都需要隱藏該加載器。
這時,可以使用 .finally() 來確保不論上傳的結果如何,都會隱藏加載器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function uploadFile(file) {
const uploadIndicator = document.getElementById("upload-indicator");
showLoadingIndicator(uploadIndicator);

return new Promise((resolve, reject) => {
// 模擬文件上傳,這裡使用setTimeout模擬非同步操作
setTimeout(() => {
const success = Math.random() > 0.5; // 模擬成功或失敗
if (success) {
resolve("文件上傳成功");
} else {
reject("文件上傳失敗");
}
}, 2000);
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
hideLoadingIndicator(uploadIndicator);
});
}

function showLoadingIndicator(indicator) {
// 顯示加載器
indicator.style.display = "block";
}

function hideLoadingIndicator(indicator) {
// 隱藏加載器
indicator.style.display = "none";
}

const fileInput = document.getElementById("file-input");
fileInput.addEventListener("change", event => {
const file = event.target.files[0];
if (file) {
uploadFile(file);
}
});

當用戶選擇文件並觸發上傳操作時,我們首先顯示了一個加載器,然後使用 Promise 來模擬文件上傳操作。不論上傳成功還是失敗,.finally() 中的代碼都會確保隱藏加載指示器,從而提供更好的用戶體驗。


async/await 是什麼?

async/await 是 JavaScript 中用於處理非同步操作的一種語法糖,
基於 Promise 提供了更好的非同步操作處理方式,使代碼更易讀且更容易理解。

async/await 的主要概念

  1. async 函式
    使用 async 關鍵字來定義非同步函式,這些函式返回一個 Promise 對象,可以處理非同步操作。

  2. await 關鍵字
    await 用於等待一個 Promise 完成。當在 async 函式中使用 await 時,該函式會暫停執行,直到等待的 Promise 完成並返回結果。這使得代碼看起來更像同步代碼,因此可以更容易地理解和維護。

  3. 錯誤處理
    使用 try…catch 構造來捕獲非同步操作中的錯誤,就像處理同步代碼一樣。

  4. 多個非同步操作的處理
    async/await 可以用於處理多個非同步操作,例如按順序執行多個非同步任務,或並行執行它們,從而使代碼的流程更容易管理。

我們可以使用剛剛外賣訂購的例子來進一步說明 async/await 的用法。
假設你要訂外賣,然後等待外賣送達,然後再享用外賣。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 模擬外賣送達的函式,返回 Promise
function deliverFood() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.2; // 模擬成功或失敗
if (success) {
resolve("外賣已送達!");
} else {
reject("對不起,配送出了問題。");
}
}, 2000); // 模擬兩秒的送達時間
});
}

// 定義一個 async 函式,訂外賣並等待送達
async function orderAndAwaitDelivery() {
try {
console.log("開始訂外賣...");
const deliveryResult = await deliverFood();
console.log(deliveryResult);
console.log("確保你有筷子、餐盤和餐巾紙。");
} catch (error) {
console.error("配送出現問題:", error);
}
}

// 執行 async 函式
orderAndAwaitDelivery();

deliverFood 函式模擬外賣送達,並返回一個 Promise。
orderAndAwaitDelivery 是一個 async 函式,內部使用 await 等待外賣送達的完成。

當我們執行 orderAndAwaitDelivery 函式時,它首先輸出 “開始訂外賣…”,然後等待外賣送達,最後根據送達的結果輸出相應的消息。如果送達過程中出現問題,則捕獲並處理錯誤。


希望這些關於 Promise 和 async/await 的解釋能讓大家更好地理解和應用這兩個重要的非同步操作概念。
在現代 JavaScript 開發中非常常見,用於處理各種非同步任務,提高代碼的效能和可讀性。


參考資料:
Promise 是什麼?有什麼用途?