文章目录

JavaScript async/await:异步函数的错误处理

发布于 2026-04-17 05:15:41 · 浏览 18 次 · 评论 0 条

JavaScript async/await:异步函数的错误处理

1. 使用 try...catch 捕获标准错误

async 函数内部抛出的错误(无论是显式 throw 还是底层网络请求失败)都会导致返回的 Promise 变为 rejected 状态。最标准的处理方式是使用 try...catch 语句块。

定义一个可能抛出错误的异步函数。模拟一个网络请求失败的场景。

async function getUserData() {
  // 模拟一个模拟 API 调用
  const response = await fetch('https://api.example.com/user/123');

  // fetch 只有在网络错误时才 reject,HTTP 404/500 依然视为 resolve
  // 需要手动检查状态并抛出错误
  if (!response.ok) {
    throw new Error(`请求失败,状态码: ${response.status}`);
  }
  
  return await response.json();
}
```

**调用**该函数时,**使用** `try...catch` 包裹 `await` 操作。

```javascript
async function main() {
  try {
    console.log('开始获取数据...');
    const data = await getUserData();
    console.log('数据获取成功:', data);
  } catch (error) {
    // 无论 getUserData 是网络断开还是我们手动 throw 的错误,都会在这里被捕获
    console.error('捕获到异常:', error.message);
    // 在这里可以执行降级逻辑,比如显示默认数据
  }
}

main();
```

---

## 2. 处理并发请求的“快速失败”与“全部完成”

在并发场景下,错误处理策略取决于你是否需要“只要有一个失败就全部取消”还是“无论成败都要拿到所有结果”。

### 场景 A:快速失败

**使用** `Promise.all` 时,只要其中一个 Promise 被拒绝,整个 `Promise.all` 就会立即拒绝。

**包裹** `await Promise.all` 并进行捕获。

```javascript
async function fetchAllCriticalData() {
  try {
    const [user, posts] = await Promise.all([
      fetch('/api/user').then(r => r.json()),
      fetch('/api/posts').then(r => r.json())
    ]);
    
    return { user, posts };
  } catch (error) {
    // 此时无法知道是 user 还是 posts 失败,只知道有东西挂了
    console.error('关键数据请求失败,流程终止:', error.message);
    // 适合:如果用户信息拿不到,文章列表也就没意义显示的情况
  }
}
```

### 场景 B:全部完成

**切换**到 `Promise.allSettled`。这个方法不会因为单个失败而中断,它会等待所有 Promise 完成(无论成功还是失败)。

**遍历**返回的结果数组,**检查**每个结果的状态。

```javascript
async function fetchAllDataSafe() {
  const results = await Promise.allSettled([
    fetch('/api/user').then(r => r.json()),
    fetch('/api/posts').then(r => r.json())
  ]);

  const successfulResults = [];
  const errors = [];

  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      successfulResults.push(result.value);
    } else {
      console.error(`请求 ${index} 失败:`, result.reason.message);
      errors.push(result.reason);
    }
  });

  // 即使有部分失败,依然可以处理成功的数据
  return { data: successfulResults, errors };
}

3. 消除嵌套地狱:使用高阶函数封装

当业务逻辑复杂,存在大量串行依赖的 await 操作时,层层嵌套的 try...catch 会导致代码缩进过深,难以维护。编写一个 to 工具函数,将错误处理转化为数组解构模式。

创建一个 to.js 文件,导出如下函数:

/**
 * 将 Promise 的错误处理转为 [error, data] 形式
 * @param {Promise} promise 
 * @returns {Promise<[Error|null, any]>}
 */
function to(promise) {
  return promise
    .then(data => [null, data])
    .catch(err => [err, null]);
}

export default to;

重构业务代码,使用这个工具函数替代 try...catch。这种写法让代码始终保持“扁平”。

import to from './to.js';

async function processOrder() {
  // 第一步:获取用户
  const [userErr, user] = await to(fetch('/api/user').then(r => r.json()));

  if (userErr) {
    console.error('无法获取用户,终止流程');
    return;
  }

  // 第二步:获取库存 (不再需要额外的 try 块包裹)
  const [stockErr, stock] = await to(fetch(`/api/stock/${user.id}`).then(r => r.json()));

  if (stockErr) {
    console.error('库存查询失败,但用户信息已获取');
    // 可以在这里执行降级操作,比如展示“库存暂时未知”
  }

  // 第三步:无论库存是否查到,都尝试记录日志
  const [logErr] = await to(fetch('/api/log', { 
    method: 'POST', 
    body: JSON.stringify({ action: 'view' }) 
  }).then(r => r.json()));

  if (logErr) {
    // 日志记录失败通常不影响主流程,仅打印警告
    console.warn('日志记录失败');
  }

  console.log('流程执行完毕');
}

4. 顶层错误兜底

在 Node.js 环境或现代前端框架中,未捕获的 Promise 拒绝可能会导致程序崩溃或白屏。注册全局的未捕获 rejection 处理器作为最后一道防线。

添加以下代码到应用的入口文件(如 app.jsindex.js)的最顶端:

// 浏览器环境
window.addEventListener('unhandledrejection', (event) => {
  console.error('全局未捕获的 Promise 拒绝:', event.reason);
  // 阻止默认的控制台报错输出(可选)
  // event.preventDefault();
});

// Node.js 环境
process.on('unhandledRejection', (reason, promise) => {
  console.error('全局未捕获的 Promise 拒绝:', reason);
  // 建议在这里进行优雅关闭或上报监控系统
});

5. 方案对比与选择

下表总结了上述三种核心处理方式的适用场景,帮助你快速决策。

处理方式 适用场景 代码可读性 错误恢复难度
try...catch 简单的线性逻辑,或者需要对不同错误执行完全不同分支时 ⭐⭐⭐⭐⭐ 简单,直接在 catch 块处理
to() 封装 长串行链路,或者希望保持代码扁平化,避免“回调金字塔” ⭐⭐⭐⭐ (需适应解构写法) 简单,通过 if (err) 判断
Promise.allSettled 批量并行请求,且不能因为单个失败而丢失其他数据 ⭐⭐⭐ 中等,需遍历结果集逐个过滤

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文