当前位置: 首页 > news >正文

Promise的allSettled,all,race

下列代码证实了:无论是for或是for of循环, 都会等上一个请求彻底完成,才会开始下一个

 // 模拟一个获取用户数据的api请求
function fetchUser(id){
return new Promise(resolve=>{
setTimeout(() =>{
console.log(`获取到用户${id}`);// 模拟网络请求resolve({id: id,name:`用户${id}`});},1000);// 假设每个请求需要1秒钟});
}// 错误做法:在循环里一个接一个地等
async function getAllUsers(userIds){
console.time('获取所有用户耗时');
const users = [];
//无论是for或是for of,都会等上一个请求彻底完成,才会开始下一个
// for(const id of userIds) {
// // 关键问题:这里会停下来等,等上一个请求彻底完成,才会开始下一个
// const user =await fetchUser(id);
//   users.push(user);
//  }for(let i=0;i<userIds.length;i++){const user =await fetchUser(userIds[i]);users.push(user);}
console.timeEnd('获取所有用户耗时'); 
return users;
}const userIds = [1,2,3,4,5];
getAllUsers(userIds);
// 控制台输出:获取所有用户耗时: 约5000毫秒

Promise.race: 只要数组里有一个Promise完成(无论是成功还是失败),它就立刻完成,结果或错误就是那个最快的Promise的。

适合做超时控制或者从多个来源取最快响应(比如测哪个CDN快)。

async function getFirstResponse(){const timeoutPromise =new Promise((_, reject) =>setTimeout(() =>reject(new Error('超时!')),500));const dataPromise = new Promise((resolve, _) =>setTimeout(() =>resolve(123),1500));try{const result =await Promise.race([dataPromise, timeoutPromise]);console.log('成功获取数据:', result);}catch(error) {console.log('出错或超时:', error);}
}
getFirstResponse()

Promise.any: 等待第一个成功完成的Promise。只有数组里所有的Promise都失败了,它才失败。适合需要尝试多个途径,只要有一个成功就行。

async function getFromAnySource(sources) {
try {
const firstSuccess = await Promise.any(sources.map(source => fetch(source)));
console.log('从最快成功的源获取:', firstSuccess);} catch (errors) { // 注意:错误是 AggregateError
console.log('所有源都失败了:', errors);}
}

Promise.allSettled:每个都要结果,不管成功失败
如果有些请求可能会失败,但你不想让一个失败就中断所有,还想知道每个请求最终是成功还是失败了,用Promise.allSettled。

本例中,fetch后面都用catch接住,所以allSettled里的结果都视作成功的结果
const apiRequests = [fetch("https://api.example.com/data1").catch(e=>e),fetch("https://api.example.com/data2").catch(e=>e),fetch("https://api.example.com/data3").catch(e=>e),
];Promise.allSettled(apiRequests).then(results => {const successfulData = results.filter(result => result.status === "fulfilled").map(result => result.value);const errors = results.filter(result => result.status === "rejected").map(result => result.reason);console.log("Successful responses:", successfulData);console.log("Errors:", errors[0]);});

控制同时请求的数量:别把服务器压垮
如果你的用户ID列表有1000个,用Promise.all会瞬间发出1000个请求。

这可能会让你的服务器崩溃,或者被浏览器限制(浏览器通常对同一域名有并发请求数限制,比如6-8个)。

这时候你需要一个“池子”来控制同时进行的请求数量。这里提供一个简单但有效的实现方法:

  async function runWithConcurrency(tasks, maxConcurrent) {
const results = []; // 存放所有任务的最终结果(Promise)
const activeTasks = []; // 当前正在执行的任务对应的Promise(用于跟踪)for (const task of tasks) {
// 1. 创建代表当前任务的Promise。`() => task()` 确保任务在需要时才启动
const taskPromise = Promise.resolve().then(task);results.push(taskPromise); // 保存结果,最后统一用 Promise.all 等// 2. 创建任务完成后的清理操作:从 activeTasks 中移除自己
const removeFromActive = () => activeTasks.splice(activeTasks.indexOf(removeFromActive), 1);activeTasks.push(removeFromActive); // 注意:这里存的是清理函数对应的Promise// 3. 如果当前活跃任务数已达上限,就等任意一个完成
if (activeTasks.length >= maxConcurrent) {
await Promise.race(activeTasks); // 等 activeTasks 数组里任意一个Promise完成}// 4. 将清理操作与实际任务完成挂钩taskPromise.then(removeFromActive, removeFromActive); // 无论成功失败都清理}// 5. 等待所有任务完成(无论是否在活跃池中)
return Promise.allSettled(results); // 或者用 Promise.all(results) 只关心成功
}// 使用示例
const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将 fetchUser(id) 调用包装成无参数的函数数组
const tasks = userIds.map(id =>() => fetchUser(id));// 最多同时发出 3 个请求
runWithConcurrency(tasks, 3).then(results => {
console.log('所有用户获取完成 (并发控制):', results);
});

这个函数会确保最多只有maxConcurrent个请求同时在进行。

当一个请求完成,池子里有空位了,才会开始下一个请求。在实际项目中,你也可以使用成熟的库如 p-limit 或 async 的 queue 方法来实现更强大的并发控制。

http://www.lryc.cn/news/599676.html

相关文章:

  • 智能网关:物联网时代的核心枢纽
  • 在OpenMP中,#pragma omp的使用
  • 【Linux/Ubuntu】VIM指令大全
  • 如何搭建Linux环境下的flink本地集群
  • 为什么选择EasyGBS?
  • 《解锁前端数据持久化与高效查询:IndexedDB深度剖析》
  • vue3单页面连接多个websocket并实现断线重连功能
  • TDengine 转化函数 TO_TIMESTAMP 用户手册
  • 比特币技术简史 第八章:扩展性解决方案 - 闪电网络与隔离见证
  • 软件工程:软件需求
  • Ethereum:告别 personal API,拥抱 Geth 的独立签名器 Clef
  • CRM 系统:实现商机全流程管理的关键
  • Mkdocs相关插件推荐(原创+合作)
  • 力扣Hot100疑难杂症汇总
  • Java环境配置之各类组件下载安装教程整理(jdk、idea、git、maven、mysql、redis)
  • 如何序列化和反序列化动态 XmlElement ?
  • 【SSL证书校验问题】通过 monkey-patch 关掉 SSL 证书校验
  • Linux725 磁盘阵列RAID0 RAID1
  • [python][flask]Flask-Login 使用详解
  • win通过OpenSSL生成.ssh id_rsa密钥方法
  • 基于libhv实现的TCP Client Server支持同步,异步传输 (C++11)
  • QT开发技术【串口和C++20协程,实现循环发送、暂停、恢复、停止】
  • 上位机知识篇---Jetson Nano的深度学习GPU推理
  • TCP模型,mqtt协议01 day41
  • 【算法-图论】图的存储
  • 嵌入式——C语言:指针①
  • Web攻防-业务逻辑篇密码找回重定向目标响应包检验流程跳过回显泄露验证枚举
  • Go 官方 Elasticsearch 客户端 v9 快速上手与进阶实践*
  • 深度学习day02--神经网络(前三节)
  • 安装本地python文件到site-packages