Promise.all、allSettled、race、any 怎么选,失败后其他请求会怎样

一个页面同时加载用户资料、通知和推荐内容。如果推荐接口失败,资料和通知是否还要展示?如果三个备用地址里有一个很快报错,页面是立即失败,还是等另一个地址成功?

Promise 的四种聚合方法,区别就体现在这些结束条件上。先说清楚页面允许什么结果,再选方法,通常比从方法名字出发更容易写对。

先比较结束条件和结果形状

方法 聚合结果什么时候成功 什么时候失败 返回什么
Promise.all 所有输入都成功 任意输入失败 按输入顺序排列的值
Promise.allSettled 所有输入都已成功或失败 普通输入 Promise 的失败会记录在结果中 按输入顺序排列的状态对象
Promise.race 最先结束的输入成功 最先结束的输入失败 该输入的值或拒绝原因
Promise.any 任意输入成功 所有输入都失败 首个成功值,或 AggregateError

这里的 allSettled 不能概括成任何情况下都不会失败。输入的迭代过程等也可能抛错。它适合收集普通任务各自的成功和失败状态。MDN Promise 文档列出了这些并发方法的规则。

all 和 allSettled 的结果顺序取决于输入顺序。如果第 2 个请求先完成,它的结果仍在数组第 2 项。完成顺序和结果排列顺序需要分开看。

必须全部拿到,还是允许局部失败

假设进入某个页面必须同时拿到权限和配置,两者缺一不可,all 可以表达这个要求。某个任务失败后,聚合 Promise 就会拒绝,调用方进入统一错误处理。

如果资料必须成功,但推荐内容可以稍后补上,就不宜把两者简单当成同样的必需任务。可以分别处理,或者用 allSettled 收集结果,再根据业务规则决定显示哪些部分。

allSettled 的输出包含 fulfilled 或 rejected 状态。失败项保留拒绝原因,成功项保留值。这样可以展示部分内容、记录失败项,或只重试其中一些任务。

javascript 复制代码
async function collectResults() {
  const jobs = [
    Promise.resolve("profile"),
    Promise.reject(new Error("recommendation unavailable")),
  ];
  const results = await Promise.allSettled(jobs);
  console.log(results.map(item => item.status));
}

collectResults().catch(console.error);
// ["fulfilled", "rejected"]

这段代码用已经成功或失败的 Promise 演示结果结构,没有模拟真实接口。页面是否能接受局部失败,仍然要由产品行为决定。

race 要最快结束,any 要第一个成功

下面用两个定时任务模拟备用服务。A 很快失败,B 稍后成功,延迟只用于演示先后顺序。

javascript 复制代码
function makeJobs() {
  return [
    new Promise((_, reject) => {
      setTimeout(() => reject(new Error("A unavailable")), 10);
    }),
    new Promise(resolve => {
      setTimeout(() => resolve("B ready"), 30);
    }),
  ];
}

async function compare() {
  try {
    console.log("race", await Promise.race(makeJobs()));
  } catch (error) {
    console.log("race", error.message);
  }
  console.log("any", await Promise.any(makeJobs()));
}

compare().catch(console.error);
// race A unavailable
// any B ready

race 很快得到失败结果,any 则继续等待可用结果。如果业务需求是从备用服务里拿到一个成功响应,这两种方法不能互换。

示例使用 JavaScript 原生 Promise API。Python 的 asyncio 有自己的任务调度和取消规则,不能把这四个方法机械对应成四段 Python 代码。

聚合结果结束,不会自动取消其他任务

all 提前失败以后,其余请求可能继续运行。race 已经返回一个结果,其他输入也可能还在等待。any 拿到成功值,同样不会自动终止剩余任务。

若底层 API 支持取消,可以结合 AbortController 等机制主动取消不再需要的请求。涉及写操作时,还要考虑服务端已经执行了多少。客户端停止等待,并不能证明服务端没有创建订单或修改数据。

超时包装也有同样的问题。把网络请求和一个拒绝的定时任务放进 race,可以限制调用方等多久,却没有自动限制服务端执行多久。需要释放资源时,超时处理还应触发底层取消,并清理定时器。

fetch 的成功状态与业务成功还要再判断

fetch 收到 404、500 等 HTTP 响应时,通常会得到一个 fulfilled 的 Response。聚合方法看到的是 Promise 状态,不会替你把 HTTP 错误判断成 rejected。

如果要让这些响应进入失败分支,需要在请求函数里检查 response.ok,按业务要求抛错,再把这个请求函数返回的 Promise 交给聚合方法。MDN Fetch 使用指南解释了这一步。

空输入和并发限制别遗漏

空输入时,all 和 allSettled 成功返回空数组,any 以 AggregateError 拒绝,race 会一直保持 pending。封装通用批处理函数时,应明确空集合的行为。

还有一个常见误区。调用请求函数时,工作可能已经开始;all 负责聚合状态,不负责把一万个请求限成五路并发,也不会自动把主线程重计算分配给多个 CPU。任务数量很多时,需要另外设计执行队列或并发上限。

我是程序员Sunday,也在整理「sunday面试指南」。对应的完整问答是 Promise 四种并发方法与取消行为,里面补充了常见面试追问。

相关推荐
niucloud-admin1 小时前
JAVA V6 多商户商城 开发文档——管理端前端
前端
weixin_422201301 小时前
如何解决小程序图标点击热区小,落点不准问题?
前端·小程序·样式·点击热区·扩大
daxiangxm1 小时前
GEO策略:让AI直接引用你的内容
开发语言·团队开发·运维开发·个人开发·数据库开发
泡海椒1 小时前
JQuick-Excel 实战:用 VALIDATION 建立可维护的 Excel 导入校验
开发语言·python·excel
longlongzihan1 小时前
前缀积与后缀积 —— LeetCode 238. 除了自身以外数组的乘积
开发语言·c++·leetcode
szial2 小时前
JavaScript 的 call、apply 和 bind:如何控制函数调用
开发语言·前端·javascript
weixin_307779132 小时前
C++代码实现MATLAB中的cvpartition函数功能
开发语言·c++·算法·matlab
黑妹天下第一乖2 小时前
小智改造实战解读-首 token 延迟去哪了:云端与端侧大模型的分段对照
开发语言·人工智能·python·嵌入式硬件·自然语言处理·iot
。小二2 小时前
Go 泛型并发利刃:async 库深度评测——370K ops/s、零依赖、生产就绪
开发语言·javascript·golang