promise.catch 是 async 函数外捕获异步异常的唯一可靠方式,因 try/catch 无法感知未 await 的 promise 拒绝,错误会触发 unhandledrejection;必须为每个未 await 的 promise 显式添加 .catch() 防止静默失败。

在 async 函数外捕获异步异常,Promise.catch 是唯一可靠且语义明确的方式。因为 try/catch 本身不感知异步拒绝,它只响应同步抛出或 await 转换后的异常;而未被 await 的 Promise,一旦 rejected,就彻底脱离函数作用域,只能靠 .catch() 拦截。
为什么 try/catch 在 async 外无效
async 函数返回的是 Promise,但调用它本身只是创建一个待定状态的对象——错误还没发生,更没进入执行栈。此时直接用 try/catch 包裹调用,等于试图抓取一个“还没抛的东西”。
- 写法:
try { apiCall() } catch(e) { ... }→ 不会捕获任何异常 - 原因:apiCall() 立即返回 Promise,reject 发生在后续微任务中,早已跳出 try 块
- 后果:触发
unhandledrejection,控制台报错,业务逻辑中断无提示
Promise.catch 是外部兜底的刚性需求
只要你不 await,Promise 就必须自己负责错误归宿。.catch() 不是可选项,而是防止静默失败的底线措施。
- 适用于所有未 await 的场景:事件回调、定时器封装、第三方库返回的 Promise
- 链式调用中,一个 .catch() 可覆盖前面所有 .then() 中的同步/异步错误
- 推荐放在末尾,例如:
fetch(...).then(...).catch(handleError)
fetch 等 API 的特殊性强化了 .catch 的必要性
fetch 成功返回 Response 对象,即使 HTTP 状态码是 404 或 500,Promise 依然 resolve。这意味着你必须手动检查 res.ok 并主动 reject,否则错误根本不会走到 .catch。
- 正确做法:
fetch(url).then(res => { if (!res.ok) throw new Error(res.status) }) - 漏掉这步 → 后续 .catch() 收不到错误 → 数据解析失败或 undefined 访问等隐性 bug
混用 async 和 Promise 链时的常见陷阱
在 async 函数里调用 Promise 方法却不 await,错误仍走 Promise 路径,不会进外层 try/catch。这时若又没配 .catch(),就等于双重放行。
- 反例:
async function f() { getUser().catch(log); return true; }→ log 执行了,但错误没中断流程,也没向上抛 - 正解:要么 await + try/catch,要么确保每个 Promise 都有 .catch(),二者不混用
- 统一风格能避免责任模糊,尤其在团队协作和中间件封装中至关重要











