javascript中Promise.allSettled()怎么用

发布时间:2021-10-15 11:07:17 作者:小新
来源:亿速云 阅读:344
# JavaScript中Promise.allSettled()怎么用

## 引言

在现代JavaScript开发中,异步编程是不可或缺的一部分。随着ES6引入Promise对象,处理异步操作变得更加优雅和高效。而在ES2020(ES11)中新增的`Promise.allSettled()`方法,则为Promise的组合处理提供了更灵活的方式。本文将深入探讨`Promise.allSettled()`的使用方法、适用场景以及与类似方法的对比。

## 一、Promise.allSettled()基础

### 1.1 方法定义

`Promise.allSettled()`是一个静态方法,接收一个Promise可迭代对象(通常是数组)作为参数,返回一个新的Promise。这个新Promise会在所有输入的Promise都"settled"(即已兑现或已拒绝)后兑现,并返回一个包含每个Promise结果的对象数组。

```javascript
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises)
  .then(results => {
    // 处理结果
  });

1.2 返回值结构

每个结果对象都有以下结构:

1.3 与Promise.all()的区别

特性 Promise.allSettled() Promise.all()
对拒绝的处理 不短路,等待所有Promise完成 短路,任一拒绝立即拒绝
返回值 始终兑现,带状态描述数组 全部兑现才兑现,否则拒绝
适用场景 需要知道所有Promise最终状态 需要所有Promise都成功

二、基本使用示例

2.1 基本用法

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => 
  setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises)
  .then((results) => 
    results.forEach((result) => console.log(result)));

// 输出:
// {status: "fulfilled", value: 3}
// {status: "rejected", reason: "foo"}

2.2 处理混合结果

const apiCalls = [
  fetch('/api/user'),
  fetch('/api/products'),
  fetch('/nonexistent')
];

Promise.allSettled(apiCalls)
  .then(results => {
    const successful = results.filter(r => r.status === 'fulfilled');
    const failed = results.filter(r => r.status === 'rejected');
    
    console.log(`${successful.length} calls succeeded`);
    console.log(`${failed.length} calls failed`);
    
    successful.forEach(res => {
      console.log('Data:', res.value);
    });
    
    failed.forEach(err => {
      console.error('Reason:', err.reason);
    });
  });

三、实际应用场景

3.1 批量API请求处理

在需要向多个API端点发送请求并希望收集所有响应(无论成功与否)时特别有用:

async function fetchMultipleEndpoints(endpoints) {
  const promises = endpoints.map(endpoint => 
    fetch(endpoint)
      .then(response => response.json())
      .catch(error => ({ error: true, message: error.message }))
  );
  
  return Promise.allSettled(promises);
}

// 使用示例
const endpoints = [
  'https://api.example.com/users',
  'https://api.example.com/products',
  'https://api.example.com/invalid'
];

fetchMultipleEndpoints(endpoints)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Endpoint ${index} success:`, result.value);
      } else {
        console.error(`Endpoint ${index} failed:`, result.reason);
      }
    });
  });

3.2 表单多字段验证

实现表单多字段独立验证,收集所有验证错误:

function validateField(field, value) {
  return new Promise((resolve, reject) => {
    // 模拟异步验证
    setTimeout(() => {
      if (field === 'username' && value.length < 5) {
        reject(`${field} must be at least 5 characters`);
      } else if (field === 'email' && !value.includes('@')) {
        reject(`${field} must be a valid email`);
      } else {
        resolve(`${field} is valid`);
      }
    }, Math.random() * 1000);
  });
}

async function validateForm(formData) {
  const validations = Object.entries(formData).map(
    ([field, value]) => validateField(field, value)
  );
  
  const results = await Promise.allSettled(validations);
  
  const errors = results
    .filter(r => r.status === 'rejected')
    .map(r => r.reason);
    
  if (errors.length > 0) {
    throw new Error(`Validation failed: ${errors.join(', ')}`);
  }
  
  return 'Form is valid';
}

// 使用示例
const form = {
  username: 'john',
  email: 'john@example',
  password: 'secret'
};

validateForm(form)
  .then(console.log)
  .catch(console.error);

3.3 并行任务执行与结果收集

async function runParallelTasks(tasks) {
  const start = Date.now();
  const results = await Promise.allSettled(tasks.map(task => task()));
  
  console.log(`All tasks completed in ${Date.now() - start}ms`);
  
  return {
    succeeded: results
      .filter(r => r.status === 'fulfilled')
      .map(r => r.value),
    failed: results
      .filter(r => r.status === 'rejected')
      .map(r => r.reason)
  };
}

// 示例任务
const tasks = [
  () => new Promise(resolve => setTimeout(() => resolve('Task 1 done'), 1000)),
  () => new Promise((_, reject) => setTimeout(() => reject('Task 2 failed'), 800)),
  () => new Promise(resolve => setTimeout(() => resolve('Task 3 done'), 1200))
];

runParallelTasks(tasks)
  .then(({ succeeded, failed }) => {
    console.log('Succeeded:', succeeded);
    console.log('Failed:', failed);
  });

四、高级用法与技巧

4.1 结合async/await

async function processMultipleRequests(urls) {
  try {
    const results = await Promise.allSettled(
      urls.map(url => fetch(url).then(res => res.json()))
    );
    
    const data = results
      .filter(result => result.status === 'fulfilled')
      .map(result => result.value);
      
    const errors = results
      .filter(result => result.status === 'rejected')
      .map(result => result.reason);
      
    return { data, errors };
  } catch (error) {
    // 这里不会捕获allSettled的异常,因为它总是resolve
    console.error('Unexpected error:', error);
    throw error;
  }
}

4.2 超时处理

function withTimeout(promise, timeout) {
  return Promise.race([
    promise,
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout)
    )
  ]);
}

async function fetchWithTimeouts(urls, timeout) {
  const requests = urls.map(url => 
    withTimeout(fetch(url), timeout)
      .then(response => response.json())
      .catch(error => ({ error: error.message }))
  );
  
  const results = await Promise.allSettled(requests);
  
  return results.map(result => 
    result.status === 'fulfilled' ? result.value : { error: result.reason }
  );
}

4.3 重试机制

async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (err) {
      if (i === retries - 1) throw err;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

async function fetchMultipleWithRetry(urls) {
  const results = await Promise.allSettled(
    urls.map(url => fetchWithRetry(url))
  );
  
  return {
    successful: results
      .filter(r => r.status === 'fulfilled')
      .map(r => r.value),
    failed: results
      .filter(r => r.status === 'rejected')
      .map(r => ({ url: r.reason.config.url, error: r.reason.message }))
  };
}

五、浏览器兼容性与polyfill

5.1 兼容性情况

Promise.allSettled()在以下环境中原生支持: - Node.js 12.9.0+ - Chrome 76+ - Firefox 71+ - Safari 13+ - Edge 79+

5.2 简单的polyfill实现

if (!Promise.allSettled) {
  Promise.allSettled = function(promises) {
    return Promise.all(promises.map(p => 
      Promise.resolve(p).then(
        value => ({ status: 'fulfilled', value }),
        reason => ({ status: 'rejected', reason })
      )
    ));
  };
}

5.3 使用core-js polyfill

npm install core-js
// 在应用入口处
import 'core-js/features/promise/all-settled';

六、性能考虑与最佳实践

6.1 性能考量

  1. 内存使用allSettled()会保留所有Promise的结果,对于大量Promise需注意内存消耗
  2. 并行限制:对于大量并发请求,考虑使用p-limit等库限制并发数
  3. 错误处理:虽然不会抛出异常,但仍需处理拒绝的Promise

6.2 最佳实践

  1. 明确使用场景:只在需要知道所有Promise最终状态时使用
  2. 结果处理:始终检查结果对象的status字段
  3. 结合finally:可配合finally进行清理操作
  4. 日志记录:对失败结果进行适当记录
Promise.allSettled(jobs)
  .then(results => {
    // 处理结果
  })
  .finally(() => {
    // 清理资源
    cleanup();
  });

七、与其他异步模式的比较

7.1 与Promise.all()对比

// 使用Promise.all() - 快速失败
Promise.all([successfulCall(), failingCall()])
  .then(console.log)
  .catch(error => {
    console.error('One failed, all fail:', error);
  });

// 使用Promise.allSettled() - 等待全部完成
Promise.allSettled([successfulCall(), failingCall()])
  .then(results => {
    const errors = results.filter(r => r.status === 'rejected');
    if (errors.length > 0) {
      console.warn(`${errors.length} calls failed`);
    }
    // 继续处理成功的结果
  });

7.2 与Promise.any()/race()对比

方法 描述 结果状态
Promise.allSettled 等待所有完成,返回所有状态 永不拒绝
Promise.race 第一个settled的Promise 可能兑现或拒绝
Promise.any 第一个兑现的Promise 全部拒绝时才拒绝

八、总结

Promise.allSettled()为JavaScript异步编程提供了重要的补充,特别适合以下场景: - 需要知道多个异步操作的全部最终结果 - 部分失败不应阻止其他操作的执行 - 需要收集错误信息进行统一处理

通过本文的示例和解释,您应该已经掌握了如何在实际项目中有效使用这一强大的API。合理运用Promise.allSettled()可以使您的异步代码更加健壮和可维护。

九、延伸阅读

  1. MDN Promise.allSettled()文档
  2. ECMAScript 2020规范
  3. JavaScript异步编程模式

”`

推荐阅读:
  1. 微信小程序与页面wepy框架布局应用的示例分析
  2. javascript:void指的是什么意思

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

javascript

上一篇:angular中的类型指令有哪几种

下一篇:JavaScript的反射学习方法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》