你必須知道的 JavaScript async/await 模式

Frontend2026-09-20TryQuickToolBox

异步程式設計是現代 JavaScript 的核心部分,而 async/await 讓它變得更加可讀。但許多開發者仍然會遇到常見的陷阱:未處理的 promise 拒絕、緩慢的循序請求,或是令人困惑的錯誤處理。在本文中,我們將探討實用的 async/await 模式,你可以立即應用這些模式來寫出更簡潔、更有效率的程式碼。

1. 基本的 async/await 語法

async 函式總是會回傳一個 Promise。await 關鍵字會暫停執行,直到 Promise 敲定,然後回傳其解析值。如果 Promise 被拒絕,它會拋出一個錯誤,你可以用 try/catch 來捕捉。

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
  }
}

這是基礎。但現實世界的情境通常需要更細緻的方法。

2. 優雅地處理錯誤

async/await 最大的優點之一,就是你可以用 try/catch 同時處理同步與异步錯誤。然而,在單一 try 區塊中要小心多個 await 呼叫:如果第一個失敗,後續的就不會執行。

為了更好的粒度,你可以將每個 await 包在各自的 try/catch 中,或使用輔助函式來避免重複的 try/catch 區塊。

async function getUser(id) {
  try {
    const user = await fetchUser(id);
    return user;
  } catch (error) {
    console.error('Failed to fetch user:', error);
    throw error;
  }
}

3. 並行執行 promises

循序地 await 多個獨立的 promises 是常見的效能錯誤。如果你需要來自多個來源的結果,請使用 Promise.all() 來並行執行它們。

// 循序:慢
const user = await fetchUser();
const posts = await fetchPosts();

// 並行:快
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);

當你想要等待所有 promises 敲定,無論它們是解析還是拒絕,請使用 Promise.allSettled()。當你想要收集結果,即使有些失敗時,這很有用。

4. 當順序很重要時,循序執行

有時你需要按順序處理項目,例如當每一步都依賴前一步時。在這種情況下,請使用 for...of 迴圈搭配 await,而不是 forEach(它不會等待异步回呼)。

for (const item of items) {
  await processItem(item);
}

如果你需要限制並行數量,可以考慮使用像 p-limit 這樣的函式庫,或實作一個簡單的佇列。

5. 避免迴圈中的 async/await 陷阱

在 Array.prototype.forEach 內使用 await 是一個經典錯誤:forEach 不會等待异步回呼,所以迴圈會在 promises 解析之前就結束。請始終使用 for...of 或 map 搭配 Promise.all。

6. 帶有逾時的 async/await

為了防止請求懸置,你可以讓一個 promise 與逾時進行競爭。

function timeout(ms) {
  return new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
}

await Promise.race([fetchData(), timeout(5000)]);

這個模式對於可能懸置的 API 呼叫很方便。

7. 异步生成器與 for await...of

异步生成器允許你异步地產生一連串的值。你可以用 for await...of 來消費它們。

async function* fetchPages() {
  let page = 1;
  while (true) {
    const data = await fetch(`/api/items?page=${page}`);
    if (data.length === 0) break;
    yield data;
    page++;
  }
}

for await (const page of fetchPages()) {
  console.log(page);
}

這對於分頁 API 或串流資料非常有用。

8. 常見錯誤與如何避免

9. 乾淨异步程式碼的最佳實踐

常見問題

async/await 與 Promises 有什麼區別?

async/await 是 Promises 的語法糖,讓异步程式碼看起來像同步。它簡化了鏈式呼叫和用 try/catch 進行錯誤處理。

我可以在 forEach 迴圈內使用 await 嗎?

不行,forEach 不會等待异步回呼。請改用 for...of 或 Promise.all 搭配 map。

如何並行執行多個异步操作?

使用 Promise.all() 來並行執行它們,並等待全部解析。

當處理 JSON API 時,你可以使用我們的 JSON Formatter 快速格式化並驗證回應。