> 技术文档 > Promise的allSettled,all,race

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) { // 注意:错误是 AggregateErrorconsole.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 方法来实现更强大的并发控制。