promise.all 和 promise.allSettled 的区别

Promise.all 与 Promise.allSettled 的区别详解

这两个方法都是用于处理多个Promise的并发执行,但在行为和结果处理上有重要区别。

📊 核心区别对比表

特性 Promise.all Promise.allSettled
失败处理 一个失败立即拒绝整个Promise 等待所有Promise完成,无论成功失败
返回值 成功值的数组 对象数组,包含状态和值/原因
适用场景 需要所有操作都成功的场景 需要知道每个操作最终结果的场景
错误处理 只能捕获第一个错误 可以获取每个Promise的详细结果

🔍 详细解析

Promise.all - "全有或全无"

// 基本语法
Promise.all([promise1, promise2, promise3, ...])
  .then(values => {
    // 所有Promise都成功时执行
  })
  .catch(error => {
    // 任何一个Promise失败时立即执行
  });

特点:

  • 如果所有Promise都成功,返回成功值的数组
  • 如果任何一个Promise失败,立即拒绝,并返回第一个错误
  • 适用于相互依赖的操作

示例:

const p1 = Promise.resolve('成功1');
const p2 = Promise.resolve('成功2');
const p3 = Promise.reject('失败!');
const p4 = Promise.resolve('成功4');

// 所有成功的情况
Promise.all([p1, p2, p4])
  .then(results => {
    console.log(results); // ['成功1', '成功2', '成功4']
  })
  .catch(error => {
    console.log('这里不会执行');
  });

// 有失败的情况
Promise.all([p1, p2, p3, p4])
  .then(results => {
    console.log('这里不会执行');
  })
  .catch(error => {
    console.log(error); // '失败!' - 立即捕获第一个错误
    // p4的结果被忽略,即使它成功了
  });

Promise.allSettled - "全部见分晓"

// 基本语法
Promise.allSettled([promise1, promise2, promise3, ...])
  .then(results => {
    // 所有Promise都完成时执行(无论成功失败)
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('成功:', result.value);
      } else {
        console.log('失败:', result.reason);
      }
    });
  });

特点:

  • 等待所有Promise完成(成功或失败)
  • 返回对象数组,每个对象包含状态和值/原因
  • 永远不会被拒绝
  • 适用于需要知道每个操作结果的场景

示例:

const p1 = Promise.resolve('成功1');
const p2 = Promise.resolve('成功2');
const p3 = Promise.reject('失败3');
const p4 = Promise.reject('失败4');

Promise.allSettled([p1, p2, p3, p4])
  .then(results => {
    console.log(results);
    // 输出:
    // [
    //   { status: 'fulfilled', value: '成功1' },
    //   { status: 'fulfilled', value: '成功2' },
    //   { status: 'rejected', reason: '失败3' },
    //   { status: 'rejected', reason: '失败4' }
    // ]
    
    // 处理结果
    const successful = results
      .filter(r => r.status === 'fulfilled')
      .map(r => r.value);
    
    const failed = results
      .filter(r => r.status === 'rejected')
      .map(r => r.reason);
    
    console.log('成功的:', successful); // ['成功1', '成功2']
    console.log('失败的:', failed);     // ['失败3', '失败4']
  });

🎯 实际应用场景

场景1:多个API请求(使用Promise.all)

// 需要所有用户数据都加载成功才能显示页面
async function loadUserDashboard(userId) {
  try {
    const [userInfo, userOrders, userPreferences] = await Promise.all([
      fetch(`/api/users/${userId}`).then(r => r.json()),
      fetch(`/api/users/${userId}/orders`).then(r => r.json()),
      fetch(`/api/users/${userId}/preferences`).then(r => r.json())
    ]);
    
    // 所有数据都成功获取,渲染仪表板
    renderDashboard(userInfo, userOrders, userPreferences);
  } catch (error) {
    // 任何一个API失败就显示错误页面
    showErrorPage('加载用户数据失败');
  }
}

场景2:批量图片上传(使用Promise.allSettled)

// 图片上传,希望知道每个文件的上传结果
async function uploadMultipleImages(files) {
  const uploadPromises = files.map(file => 
    uploadImage(file).catch(error => {
      // 捕获单个上传错误,但继续其他上传
      return { error: error.message, fileName: file.name };
    })
  );
  
  const results = await Promise.allSettled(uploadPromises);
  
  const summary = {
    successful: [],
    failed: []
  };
  
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      if (result.value.error) {
        summary.failed.push({
          fileName: result.value.fileName,
          error: result.value.error
        });
      } else {
        summary.successful.push(result.value);
      }
    } else {
      summary.failed.push({
        error: result.reason.message
      });
    }
  });
  
  console.log(`上传完成: ${summary.successful.length} 成功, ${summary.failed.length} 失败`);
  return summary;
}

场景3:表单多个验证(混合使用)

async function validateForm(formData) {
  // 这些验证可以并行执行,但需要全部通过
  const validationPromises = [
    validateEmail(formData.email),
    validatePassword(formData.password),
    validateUsername(formData.username)
  ];
  
  try {
    const results = await Promise.all(validationPromises);
    return { isValid: true, errors: [] };
  } catch (error) {
    // 但我们需要知道所有验证错误,而不仅仅是第一个
    // 所以更好的做法是使用 allSettled
    const results = await Promise.allSettled(validationPromises);
    
    const errors = results
      .filter(result => result.status === 'rejected')
      .map(result => result.reason);
    
    return { isValid: false, errors };
  }
}

🔧 实用工具函数

1. 带超时的Promise.all

function promiseAllWithTimeout(promises, timeoutMs) {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('操作超时')), timeoutMs);
  });
  
  return Promise.all([...promises, timeoutPromise]);
}

2. 分类处理结果

function categorizeResults(results) {
  return {
    fulfilled: results.filter(r => r.status === 'fulfilled').map(r => r.value),
    rejected: results.filter(r => r.status === 'rejected').map(r => r.reason),
    all: results
  };
}

// 使用
Promise.allSettled(promises)
  .then(categorizeResults)
  .then(({ fulfilled, rejected }) => {
    console.log(`成功: ${fulfilled.length}, 失败: ${rejected.length}`);
  });

3. 逐步降级策略

async function fallbackStrategy(primaryAction, fallbackActions) {
  try {
    return await primaryAction();
  } catch (error) {
    // 主操作失败,尝试所有备用方案
    const results = await Promise.allSettled(fallbackActions.map(action => action()));
    
    const firstSuccess = results.find(result => result.status === 'fulfilled');
    if (firstSuccess) {
      return firstSuccess.value;
    }
    
    // 所有备用方案都失败
    throw new Error('所有操作都失败了');
  }
}

💡 选择指南

使用 Promise.all 当:

  • 所有操作都必须成功才能继续
  • 操作之间有依赖关系
  • 需要原子性操作(要么全成功,要么全失败)

使用 Promise.allSettled 当:

  • 需要知道每个操作的最终状态
  • 操作之间相互独立
  • 希望部分失败不影响其他操作的结果收集
  • 进行批量操作,需要生成汇总报告

记住这个简单的规则:"要么都要,要么都不要"用all,"我全都要知道"用allSettled

posted @ 2025-11-05 16:52  dirgo  阅读(9)  评论(0)    收藏  举报