你应该了解的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. 并行运行promise

顺序等待多个独立的promise是一个常见的性能错误。如果你需要来自多个来源的结果,使用Promise.all()来并发运行它们。

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

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

当你想要等待所有promise敲定,无论它们是解析还是拒绝时,使用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不会等待异步回调,所以循环在promise解析之前就结束了。始终使用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和Promise有什么区别?

async/await是Promise的语法糖,使异步代码看起来像同步代码。它通过try/catch简化了链式调用和错误处理。

我可以在forEach循环中使用await吗?

不,forEach不会等待异步回调。改用for...of或Promise.all配合map。

如何并行运行多个异步操作?

使用Promise.all()并发运行它们并等待所有解析。

处理JSON API时,你可以使用我们的JSON Formatter快速格式化和验证响应。