这是一个非常经典且实际的问题。答案简而言之是:通常需要,但不是唯一的写法。
使用 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 或者上报监控
}