JavaScript 系列 EP.05

Promise & Async:
異步處理的演進史

從混亂的 Callback Hell 到優雅的 Async/Await。徹底掌握 JavaScript 處理資料請求與長時間任務的終極方案。

發展歷程
狀態機制
錯誤處理
✍️ Joseph Chen

痛點:歡迎來到「回呼地獄」

在 Promise 出現之前,我們要處理多個相依的非同步任務,程式碼會長得像這樣:

Callback Hell 範例
getData((user) => {
  getOrders(user.id, (orders) => {
    getDetails(orders[0].id, (details) => {
      process(details, (result) => {
        console.log("終於拿到了!");
      });
    });
  });
});

為什麼這很糟糕?

1. 難以閱讀與維護

程式碼不斷向右偏移,形成所謂的「金字塔」結構。

2. 錯誤處理極其困難

每一層都要寫一次 error callback,漏掉一個就導致整個程式崩潰。

一、Promise:一份未來的契約

Promise 本質上是一個「容器」,裡面存著某個未來才會結束的事件(通常是非同步操作)的結果。

Promise 狀態機演練

Pending (等候中)

Fulfilled (成功)

Rejected (失敗)

如何創建一個 Promise
const myPromise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("任務完成!");
  } else {
    reject("發生錯誤...");
  }
});

myPromise
  .then(res => console.log(res))
  .catch(err => console.error(err));

二、Promise Chaining:鏈結的力量

Promise 最強大的地方在於 `.then()` 會回傳一個新的 Promise,這讓我們可以把非同步操作「扁平化」。

解決 Callback Hell 的寫法
getData()
  .then(user => getOrders(user.id))
  .then(orders => getDetails(orders[0].id))
  .then(details => process(details))
  .then(result => console.log("拿到結果:", result))
  .catch(err => console.error("中間任何一步失敗都會到這裡"));

錯誤處理的三個關鍵點

1

一網打盡

最後一個 .catch() 可以捕捉到鏈結中任何一個步驟拋出的錯誤。

2

斷點復原

你可以在鏈結中間插入 .catch(),回傳一個預設值,讓後續的 .then() 繼續跑。

3

Finally

無論成功或失敗,.finally() 都會執行,適合用來關閉 Loading 狀態。

三、進階技:並發處理與賽跑

當我們需要同時處理多個請求時,Promise 提供了幾個靜態方法。

Promise.all()

「全部都要成功才算成功。」
適合一次抓取多份相依資料,若有一個失敗則全部失敗。

Promise.race()

「誰跑最快就聽誰的。」
適合用來實作請求超時 (Timeout) 機制。

Promise.all 實戰範例
const [users, posts, config] = await Promise.all([
  fetchUsers(),
  fetchPosts(),
  fetchConfig()
]);

四、Async / Await:終極優雅

ES2017 推出的 `async/await` 讓我們能用同步的寫法處理非同步。它本質上是 Promise 的語法糖,讓程式碼更直覺、更好讀。

這是目前的標準寫法
async function handleData() {
  try {
    const user = await getData();
    const orders = await getOrders(user.id);
    console.log("我的訂單:", orders);
  } catch (error) {
    console.error("出錯了:", error);
  } finally {
    setLoading(false);
  }
}
必須注意的細節:
  • Await 會暫停執行:在同一個函式內,await 之後的程式碼要等前面的跑完才會動。

  • Try...Catch:異步錯誤必須被捕捉,否則會導致 Unhandled Promise Rejection。

總結:踏上異步的大師之路

JavaScript 的異步處理從「回呼」進化到「契約 (Promise)」,最後到達了「同步化 (Async/Await)」。掌握這些工具,你就能在處理複雜的網路請求、檔案操作與定時任務時,保持程式碼的清晰與穩定。

#JavaScript
#Promise
#Async
#Await
#CallbackHell
#Frontend
#V8