2026年2月5日

Async await要不要try catch?

这是一个非常经典且实际的问题。答案简而言之是:通常需要,但不是唯一的写法

使用 async/await 的核心目的是为了让异步代码看起来像同步代码。在同步代码中,我们处理异常的标准方式就是 try...catch。但是,盲目地给每个 await 包裹 try...catch 会导致代码臃肿。

以下是关于 “要不要写 try-catch” 的详细分析和最佳实践:

1. 必须要处理错误的情况 (必须 Catch)

如果你不捕获错误,当 Promise 被 reject 时,程序可能会抛出 UnhandledPromiseRejectionWarning,甚至导致整个应用崩溃(Node.js)或前端页面白屏。

场景 A:标准的 try...catch 写法

这是最推荐的通用写法,特别是当你有多个异步操作依赖于彼此时,或者你需要在一个代码块中同时捕获异步和同步的错误。

JavaScript

async function getUserData() {
  try {
    // 这里如果 fetch 失败,或者 parsing 失败,都会被 catch 捕获
    const response = await fetch('/api/user');
    const user = await response.json();
    
    // 甚至这里的同步代码报错也能被捕获
    console.log(user.name.toUpperCase()); 
  } catch (error) {
    // 统一处理错误:比如弹窗提示用户
    console.error("获取用户失败:", error);
    showErrorToast(error.message);
  } finally {
    // 比如关闭 Loading 动画
    stopLoading();
  }
}

2. 可以不写 try...catch 的情况 (混合写法)

有时候为了写一个简单的逻辑,写五六行 try...catch 显得太啰嗦。你可以直接在 await 的 Promise 后面接 .catch()

场景 B:使用 .catch() 进行兜底

这种写法适合单一的异步操作,且错误处理逻辑非常简单(比如只给个默认值)。

JavaScript

// 写法 1: 啰嗦的 try-catch
let user;
try {
  user = await fetchUser();
} catch (e) {
  user = null;
}

// 写法 2: 优雅的混合写法 (推荐)
// 如果报错,直接返回 null,不中断代码执行
const user = await fetchUser().catch(err => {
    console.log(err);
    return null; 
});

if (!user) return; // 后续逻辑

3. “Try-Catch 地狱” 的解决方案

如果你发现代码里全是 try...catch,一层套一层,可以尝试更高级的模式。

场景 C:Go 语言风格 (Await Wrapper)

这是一种流行的模式,写一个辅助函数来返回 [error, data],这样就可以像 Go 语言一样处理错误,避免缩进过深。

JavaScript

// 辅助函数
const to = (promise) => {
  return promise
    .then(data => [null, data])
    .catch(err => [err, null]);
}

async function main() {
  // 看起来非常扁平,没有 try-catch 块
  const [err, user] = await to(fetchUser());
  
  if (err) {
    return console.error('出错了', err);
  }

  console.log('成功拿到用户', user);
}

4. 总结:什么时候用哪个?

场景推荐写法理由
复杂业务逻辑try...catch可以同时捕获多步操作的错误,逻辑清晰。
简单的 API 调用await fn().catch()代码更简洁,一行搞定默认值或错误日志。
顶层调用 (如 React useEffect).catch()顶层通常不需要对外抛出错误,直接处理掉即可。
极度厌恶嵌套Wrapper 模式代码极其扁平,可读性高,但需要引入额外工具函数。

核心建议

不要让错误“吞没”。

最糟糕的做法是写了 try...catch 但在 catch 块里什么都不做(或者只打印个 log 就不管了),这会导致难以调试的 Bug。

示例(错误示范):

JavaScript

try {
  await sensitiveOperation();
} catch (e) {
  // 千万别留空!至少要 console.error 或者上报监控
}