JavaScript

梗概

Promise 的 catch 方法用于处理 Promise 链中的错误,其返回值由传入的回调函数决定,可以返回正常值或抛出新的异常,这直接影响后续 Promise 链的执行流程。

语法

promise.catch(onRejected);

其中 onRejected 是当 Promise 被拒绝(rejected)时调用的回调函数。

返回值规则

1. 回调函数返回普通值

当 catch 的回调函数返回一个普通值时,catch 方法会返回一个新的已解决(resolved)状态的 Promise,其值为回调函数的返回值。

Promise.reject('错误')
  .catch(err => {
    console.log('捕获到错误:', err);
    return '恢复正常'; // 返回普通值
  })
  .then(value => {
    console.log('继续正常流程:', value); // 输出: 继续正常流程: 恢复正常
  });

2. 回调函数返回 Promise

如果 catch 回调函数返回一个 Promise,catch 方法返回的 Promise 会跟随这个新 Promise 的状态。

Promise.reject('错误')
  .catch(err => {
    console.log('捕获到错误:', err);
    return Promise.resolve('新的异步操作成功');
  })
  .then(value => {
    console.log('继续正常流程:', value); // 输出: 继续正常流程: 新的异步操作成功
  });

3. 回调函数抛出异常

如果 catch 回调函数抛出异常,catch 方法返回的 Promise 会变为拒绝(rejected)状态。

Promise.reject('第一个错误')
  .catch(err => {
    console.log('捕获到错误:', err);
    throw '第二个错误'; // 抛出新的异常
  })
  .catch(err => {
    console.log('捕获到第二个错误:', err); // 输出: 捕获到第二个错误: 第二个错误
  });

实际应用

错误恢复

fetchData()
  .catch(err => {
    console.error('网络请求失败:', err);
    return backupData; // 使用备用数据继续流程
  })
  .then(data => processData(data));

错误转换

fetchData()
  .catch(err => {
    if (err.status === 404) {
      throw new NotFoundError('资源不存在');
    } else if (err.status === 403) {
      throw new PermissionError('权限不足');
    }
    throw err; // 其他错误直接传递
  })
  .catch(err => {
    if (err instanceof NotFoundError) {
      // 处理特定类型的错误
    } else {
      // 处理通用错误
    }
  });

条件分支处理

processTask()
  .then(result => {
    // 正常处理
    return normalProcessing(result);
  })
  .catch(err => {
    if (err.recoverable) {
      // 可恢复错误,继续流程
      console.warn('恢复处理:', err);
      return alternativeProcessing();
    }
    // 不可恢复错误,重新抛出
    throw err;
  })
  .then(finalResult => {
    // 正常流程和可恢复错误流程都会到达这里
  })
  .catch(fatalErr => {
    // 只有不可恢复的错误会到达这里
  });

最佳实践

  1. 总是在 Promise 链末尾添加 catch 处理器,避免未捕获的异常
  2. 在 catch 中明确返回一个值,以控制后续流程的行为
  3. 当需要中断流程时,使用 throw 抛出异常
  4. 根据业务逻辑需要,合理设计 catch 的位置(是在每个步骤后还是在整个流程末尾)

相关概念

Promise的catch方法

  • .catch方法实际上就相当于.then(null, rejection).then(undefined, rejection),也就是then的第二个回调函数
  • 这是一种语法糖,提供了更清晰的错误处理方式
// 这两种写法是等价的
promise.then(onFulfilled, onRejected);
 
promise.then(onFulfilled).catch(onRejected);
  • 使用.catch的好处是可以捕获前面then方法执行中的错误,更接近于同步的try/catch写法
  • 另外,.then方法中的错误会被”吃掉”(如果没有提供第二个参数),而.catch方法则可以捕获这些被”吞掉”的错误
// 推荐写法
promise
  .then(function(data) {
    // 处理成功结果
  })
  .catch(function(err) {
    // 处理前面可能出现的错误
  });

示例

const promise = new Promise((resolve, reject) => {
  reject(new Error('出错了'));
});
 
// 方式1:使用then的第二个参数
promise.then(
  result => console.log('成功:', result),
  error => console.log('失败:', error)
);
 
// 方式2:使用catch方法(推荐)
promise
  .then(result => console.log('成功:', result))
  .catch(error => console.log('失败:', error));

链接关系