前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Promise.all 单个失败保留其他结果
JavaScript异步与 Promise

Promise.all 里一个请求挂了,怎么让其他请求的结果还能用?

有两种办法。一是给每个 Promise 先接一个 .catch(),把失败转成一个普通值,这样 Promise.all 就不会被拒绝;二是直接换成 Promise.allSettled,它会等所有任务结束,每一项告诉你是成功还是失败。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#异步与 Promise#异步编程
先看核心答案读代码示例
理解线索

让失败不再传染给整体

  1. 单项兜底p.catch(err => fallback) 把拒绝变成兑现,Promise.all 看到的全是成功
  2. Promise.allSettled等所有任务落定,结果里每项带 status 字段
  3. 失败标记兜底值要能和正常数据区分开,比如带 error 字段的对象

Promise.all 失败时,其他请求并没有被取消,它们照样发完、照样返回,只是结果被丢掉了。想保住结果,只能在它们汇总到 Promise.all 之前把失败拦下来。

核心回答

先记住这个答案

Promise.all 的规则是只要有一个任务 reject,它就立刻 reject,已经成功的结果也拿不到。想让其他结果可用,就要在任务汇总之前把失败拦下来。第一种办法是给每个 Promise 接一个 .catch(),失败时返回一个兜底值,这样传给 Promise.all 的全是会成功的 Promise。第二种办法是直接用 Promise.allSettled,它等所有任务都结束,每一项用 status 告诉你成功还是失败。

  • Promise.all 一项失败就整体 reject,成功结果全丢
  • 每项先 .catch() 兜底,失败变成普通值
  • Promise.allSettled 天生不会 reject,按 status 区分
  • 关键接口用 Promise.all,可选接口单独兜底

给每个请求单独兜底

比如一个首页同时拉用户信息、订单、推荐三个接口。推荐接口挂了,你肯定不希望整个首页白屏,用户和订单照样要展示。

先 .catch() 再交给 Promise.allJavaScript
const request = (name, ok, ms) =>
  new Promise((resolve, reject) => {
    setTimeout(() => (ok ? resolve(name + '数据') : reject(new Error(name + '接口 500'))), ms)
  })

async function loadPage() {
  const tasks = [request('用户', true, 30), request('订单', false, 10), request('推荐', true, 20)]
  // 给每个任务单独兜底:失败时返回一个带标记的对象,不再往外抛
  const safe = tasks.map((p) => p.catch((err) => ({ error: err.message })))
  const results = await Promise.all(safe) // 永远不会 reject
  results.forEach((r) => console.log(typeof r === 'string' ? r : '失败:' + r.error))
}
loadPage()
查看输出与解释
用户数据
失败:订单接口 500
推荐数据

关键在 p.catch(...) 这一步。catch 返回的是一个新的 Promise,只要回调里没有再抛错,这个新 Promise 就是 fulfilled,值是回调的返回值。所以订单接口虽然失败了,到 Promise.all 手里已经变成了一个普通对象 { error: '订单接口 500' }。结果数组的顺序和传入顺序一致,不受谁先完成影响。 这种写法的好处是兜底值由你决定。比如推荐接口失败就返回空数组 [],渲染代码不用改,页面上推荐区块显示为空就行。

直接用 Promise.allSettled

ES2020 加入的 Promise.allSettled 就是为这个场景设计的。它不会 reject,等所有任务都落定后,返回一个数组,每一项是 { status: 'fulfilled', value } 或者 { status: 'rejected', reason } 这样的对象。

按 status 把成功和失败分开JavaScript
const request = (name, ok, ms) =>
  new Promise((resolve, reject) => {
    setTimeout(() => (ok ? resolve(name + '数据') : reject(new Error(name + '接口 500'))), ms)
  })

Promise.allSettled([request('用户', true, 30), request('订单', false, 10), request('推荐', true, 20)])
  .then((results) => {
    const ok = results.filter((r) => r.status === 'fulfilled').map((r) => r.value)
    const failed = results.filter((r) => r.status === 'rejected').map((r) => r.reason.message)
    console.log('成功:' + ok.join('、'))
    console.log('失败:' + failed.join('、'))
  })
查看输出与解释
成功:用户数据、推荐数据
失败:订单接口 500

和单项 .catch() 比,Promise.allSettled 保留了原始的错误对象,适合要上报错误、要统计失败率的场景。缺点是拿到结果后还得自己拆一遍,不像兜底值那样可以直接用。

两种写法怎么选
需要给失败项一个默认值、让后面代码照常跑,用单项 .catch()。需要知道到底哪些失败了、失败原因是什么,用 Promise.allSettled。两者在「保住其他结果」这件事上效果一样。

关键接口和可选接口混在一起

真实页面里接口的地位不一样。比如商品详情页,商品信息接口挂了,页面就没法看,应该整体报错;评论、推荐挂了,只影响一个区块。

这时不要一刀切。关键接口保持原样,可选接口各自接 .catch() 返回默认值,再一起放进 Promise.all。这样商品接口失败时整体 reject,走错误页;评论接口失败时整体照常成功,评论区显示「暂无评论」。一个 Promise.all 就把两种策略都表达清楚了。

回答前,多想一步

容易答错的地方

以为 Promise.all 失败后其他请求会被取消
不会。Promise 没有取消机制,Promise.all reject 只是不再关心其他结果,那些请求照样发完,服务端照样处理。真要中断,得配合 AbortController 手动调用 abort()。
兜底时返回 null,下游分不清失败和空数据
p.catch(() => null) 看起来简洁,但如果接口正常时也可能返回 null,下游就不知道到底是失败还是真没数据。兜底值最好带上标记,比如 { error } 对象,或者直接用 Promise.allSettled。
在 Promise.all 外面包 try...catch 就以为能保住结果
try...catch 能接住 Promise.all 的拒绝,但进到 catch 时你手里只有那一个错误,成功的结果已经没了。拦截必须发生在每个任务上,不能发生在汇总之后。
试着用自己的话回答

面试官还会怎么问?

不支持 Promise.allSettled 的环境怎么办?

用 Promise.all 加单项 .catch() 就能模拟出来:每个 Promise 接 .then(value => ({ status: 'fulfilled', value }), reason => ({ status: 'rejected', reason })),再交给 Promise.all。这也是很多 polyfill 的实现思路。

失败的那个请求想自动重试怎么写?

在单项兜底的位置换成重试逻辑:先调用一次,catch 里判断次数,没到上限就再调用自己,到了上限再返回兜底值。这样重试只影响那一个任务,其他任务的结果照常汇总。

只要有一个成功就行,该用哪个方法?

用 Promise.any。它和 Promise.all 刚好相反:有一个成功就兑现,全部失败才以 AggregateError 拒绝。适合多个镜像地址取最快可用那个的场景。

从一道题,走向一组知识

把知识连起来

异步与 Promise

Promise.allSettled 返回的数组里,每一项长什么样?

用 Promise.allSettled 保住结果后,要知道每一项的 status、value、reason 结构才能正确取值。

异步与 Promise

已经拿到一个 Promise 了,为什么还用 new Promise 包一层是反模式?

给每个请求兜底时直接接 catch 就行,不要再套一层 new Promise,那是另一个常见反模式。

参考资料

  • JavaScript:异步与 Promise参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 给每个请求单独兜底
  3. 直接用 Promise.allSettled
  4. 关键接口和可选接口混在一起
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑