Promise 和 async/await 被 try-catch 包裹时的行为差异

在 JavaScript 中,try-catch 是同步 的,而 Promise 的 rejection 是异步的,这导致了两者的行为有很大区别。下面我用实际例子详细说明。

1. 普通 Promise 被 try-catch 包裹(捕获不到)

js 复制代码
try {
  Promise.reject(new Error('失败了'));
  // 或者
  new Promise((resolve, reject) => {
    reject(new Error('reject 调用'));
  });
} catch (err) {
  console.log('捕获到了吗?', err.message);
}

// 结果:catch 不会执行
// 控制台会报:UnhandledPromiseRejectionWarning(未处理的 Promise 拒绝)

原因 :Promise.reject() 和 reject() 是异步触发的,try-catch 捕获不到异步错误。


2. Promise 执行器(executor)里同步 throw(可以捕获)

js 复制代码
try {
  new Promise((resolve, reject) => {
    throw new Error('同步抛出'); // 注意是 throw,不是 reject
  });
} catch (err) {
  console.log('捕获成功:', err.message); // 可以捕获
}

注意 :只有在 new Promise 的执行器函数里同步 throw,才会被外层的 try-catch 捕获。一旦变成 reject(),就捕获不到了。


3. async/await + try-catch(推荐写法,可以捕获)

js 复制代码
async function getData() {
  try {
    const result = await Promise.reject(new Error('请求失败'));
    console.log(result);
  } catch (err) {
    console.log('成功捕获异步错误:', err.message); // '请求失败'
  }
}

getData();

核心机制:

  • await 会把 rejected 的 Promise 转换成 throw
  • 因此 try-catch 就能正常捕获了

4. 调用 async 函数时使用 try-catch(捕获不到)

js 复制代码
async function getData() {
  return await Promise.reject(new Error('失败'));
}

try {
  getData();           // 没有 await!
} catch (err) {
  console.log('能捕获吗?'); // 捕获不到
}

原因 :getData() 立即返回一个 Promise,错误是异步发生的,try-catch 无法捕获。

必须改成下面这样才行:

js 复制代码
try {
  await getData();     // 加上 await 才行
} catch (err) {
  console.log('现在能捕获了');
}

总结对比表

场景 是否能被 try-catch 捕获 说明
Promise.reject() / reject() + try-catch ❌ 不能 异步错误
new Promise 里同步 throw + try-catch ✅ 能 同步错误
async 函数内部 await + try-catch ✅ 能 await 把 rejection 转成 throw
调用 async 函数(无 await)+ try-catch ❌ 不能 返回的是 Promise
顶层模块使用 await + try-catch(支持 top-level await 的环境) ✅ 能 相当于在 async 上下文中

最佳实践建议

  1. 推荐写法(async 函数内部):

    js 复制代码
    async function foo() {
      try {
        const res = await fetch('/api');
        return await res.json();
      } catch (err) {
        console.error('出错了', err);
        throw err; // 或返回默认值
      }
    }
  2. 或者使用 .catch() 方法(链式写法):

    js 复制代码
    fetch('/api')
      .then(res => res.json())
      .catch(err => console.error(err));
  3. 永远不要让 Promise rejection 变成 unhandled,这会导致程序状态不可预测。

相关推荐
临期冰淇淋3 分钟前
MTK平台 安卓16 GMS报错修改
android·java·前端
人道领域3 分钟前
【面试八股速通篇01】锁与并发工具
java·开发语言·面试·c#
Misnearch12 分钟前
React中的回调
前端·javascript·react.js
Profile排查笔记16 分钟前
浏览器多开怎么设置独立 IP?Profile、代理与核验流程
前端·人工智能·后端·自动化
考研保研资料分享1 小时前
2022数学保研经验:211专业第一的中科大、浙大、南开等五校考核复盘
面试
anyup_前端梦工厂1 小时前
将uni-app工程迁移uni-app x,AI 能帮我们做什么?
前端·人工智能·uni-app
十年Java程序媛1 小时前
Redis 分布式锁 + GZIP 压缩缓存踩坑|高并发缓存真实线上复盘
java·开发语言·spring boot·redis·分布式·缓存·面试
凌云若寒1 小时前
BarTender官网下载打印机驱动
运维·服务器·前端·网络·产品经理
zd8451015001 小时前
基于 HTML 与 Modbus TCP 构建设备仪器上位机:可行性分析与代码结构设计
前端·tcp/ip·html
Amumu121382 小时前
NodeJs介绍
前端·node.js