前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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.race 空数组
JavaScript异步与 Promise

Promise.race 传一个空数组进去,会返回什么?

返回一个永远 pending 的 Promise。Promise.race 要等数组里第一个完成的 Promise 来决定结果,空数组里一个都没有,也就永远等不到。它不会报错,then、catch、finally 都不会执行,await 它的代码会一直停在那一行。

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

空数组下四个组合方法的结果

  1. Promise.race等第一个完成的元素,没有元素就永远等下去
  2. Promise.all 和 Promise.allSettled等全部完成,零个元素等于已经全部完成,立刻以 [] 成功
  3. Promise.any等第一个成功,零个元素等于全部失败,立刻以 AggregateError 失败

永远 pending 比报错更难排查。它不会进 catch,也不会触发未处理拒绝,控制台什么都没有,只是后面的代码悄悄不执行了。

核心回答

先记住这个答案

Promise.race([]) 会返回一个永远处于 pending 的 Promise。race 的规则是谁先完成就跟谁,它内部会给数组里每个元素挂上 then,第一个回调触发时决定结果。空数组里没有元素,就没有任何回调会触发,返回的 Promise 也就永远不会成功或失败。这和 Promise.all([]) 立刻以 [] 成功、Promise.any([]) 立刻失败都不一样,面试时常把这几个放在一起问。

  • Promise.race([]) 永远 pending,不报错也不结束
  • Promise.all([]) 立刻以空数组成功
  • Promise.any([]) 立刻以 AggregateError 失败
  • 数组可能为空时,调用 race 前先判断长度

四个组合方法在空数组下的表现

同样传空数组,四个方法给出了三种结果。放在一起跑一遍最直观。

四个方法同时传空数组JavaScript
Promise.all([]).then((v) => console.log('all:', JSON.stringify(v)))
Promise.allSettled([]).then((v) => console.log('allSettled:', JSON.stringify(v)))
Promise.any([]).catch((e) => console.log('any:', e.name, e.errors.length))
Promise.race([]).then(
  () => console.log('race 成功'), // 永远不会执行
  () => console.log('race 失败') // 也永远不会执行
)
setTimeout(() => console.log('1 秒后 race 仍然没有结果'), 1000)
查看输出与解释
all: []
allSettled: []
any: AggregateError 0
1 秒后 race 仍然没有结果

race 的两个回调都没打印,不管等多久都一样。 这三种结果背后是同一个道理:看「等到什么时候算结束」。all 和 allSettled 要等全部完成,零个元素可以认为已经全部完成,所以立刻成功,结果是空数组。any 要等第一个成功,如果全部都失败了就报错,零个元素可以认为已经全部失败,所以立刻以 AggregateError(一种把多个错误打包在一起的错误类型)失败,errors 是空数组。race 要等第一个完成,没有元素就永远等不到第一个,而它又没有「全部结束」这个兜底条件,只能一直 pending。

业务代码里怎么踩到、怎么防

没人会手写 Promise.race([]),出问题的都是数组来自动态数据的时候。比如从多个 CDN 节点里选响应最快的一个,节点列表是接口下发的;某次配置出错返回了空列表,await Promise.race(list) 就卡住了,页面停在 loading,没有报错,也没有日志。

调用前先判断长度JavaScript
function delay(ms, value) {
  return new Promise((resolve) => setTimeout(() => resolve(value), ms))
}

function fastest(tasks) {
  if (tasks.length === 0) return Promise.reject(new Error('没有可用的节点')) // 空列表明确失败
  return Promise.race(tasks)
}

fastest([]).catch((e) => console.log('失败:', e.message))
fastest([delay(300, 'A'), delay(100, 'B')]).then((v) => console.log('最快:', v))
查看输出与解释
失败: 没有可用的节点
最快: B

空列表时是返回失败还是返回一个默认值,要看业务:选节点这种没有就不能继续的,应该失败;可有可无的,可以直接 Promise.resolve(默认值)。关键是不能让它悄悄 pending。

超时兜底
另一种做法是把超时也放进 race 里,比如 Promise.race([...tasks, timeout(5000)]),这样数组至少有一个元素,最坏 5 秒后也会以超时失败。这种写法一般本来就要加,用来防某个请求一直不回来,顺带也防住了空数组。
回答前,多想一步

容易答错的地方

以为空数组会立刻成功或者报错
很多人类比 Promise.all([]),以为 race 也会立刻以 undefined 成功,或者以为它会抛 TypeError。都不对,它不成功、不失败、不报错。只有传入的不是可迭代对象,比如 Promise.race(null),才会以 TypeError 拒绝。
以为 pending 的 Promise 会超时或被回收时报错
Promise 本身没有超时机制,pending 可以一直持续到页面关闭。它也不会触发 unhandledrejection 事件,因为根本没有拒绝。如果不再有引用指向它,它会被垃圾回收,挂在上面的 then 回调和 await 之后的代码就再也没有机会执行了。
试着用自己的话回答

面试官还会怎么问?

手写 Promise.race 时,空数组要特殊处理吗?

不用。标准写法是返回 new Promise((resolve, reject) => { for (const p of list) Promise.resolve(p).then(resolve, reject) }),数组为空时循环一次都不执行,resolve 和 reject 都不会被调用,自然就是永远 pending,和规范行为一致。

race 的数组里有一个普通值,比如 Promise.race([1, delay(100)]),结果是什么?

结果是 1。普通值会先被 Promise.resolve 包成一个已经成功的 Promise,它的 then 回调在下一个微任务就触发,比任何定时器都快。

race 选出结果之后,其他 Promise 会被取消吗?

不会。race 只是不再关心其他结果,那些请求和定时器照样执行完。真要取消,需要配合 AbortController,拿到结果后调用 abort()。

从一道题,走向一组知识

把知识连起来

异步与 Promise

多个 Promise 并发执行时,then 回调一定按完成顺序触发吗?

race 的结果由谁先完成决定,想弄清「谁先」就要知道 then 回调按什么顺序触发。

异步与 Promise

Promise 已经兑现了,then 回调为什么还是异步执行?

即使 race 的元素已经完成,结果也要通过异步的 then 回调才能拿到,不会同步返回。

参考资料

  • JavaScript:异步与 Promise参考资料

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

本题目录
  1. 先记住这个答案
  2. 四个组合方法在空数组下的表现
  3. 业务代码里怎么踩到、怎么防
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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