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

Promise.all 的数组里混了普通值,会怎么处理?

不会报错。Promise.all 会对每一项调用 Promise.resolve,普通值被包成一个立刻兑现的 Promise,结果数组里还是原来的值、原来的位置。就算整个数组全是普通值,结果也要在 then 里异步拿到。

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

包一层再等,值不变

  1. Promise.resolve普通值包成已兑现的 Promise,原生 Promise 原样返回
  2. thenable带 then 方法的对象会被调用 then,取它交出来的值
  3. 按索引写入每项兑现后写回自己的下标,结果顺序等于传入顺序

包装只影响「怎么等」,不影响「值是什么」。但如果某个普通对象恰好有一个叫 then 的方法,它会被当成 Promise 去调用,结果可能完全不是你放进去的那个对象。

核心回答

先记住这个答案

Promise.all 不要求每一项都是 Promise。它会对每一项调用 Promise.resolve:原生 Promise 原样返回,普通值被包成一个已兑现的 Promise,带 then 方法的对象(thenable,长得像 Promise 的对象)会被当成 Promise 去等。所以普通值在结果数组里原样出现,位置和传入时一样。整体兑现依然是异步的,哪怕数组里全是普通值,也要在 then 或 await 里才能拿到结果。

  • 每一项都会过一遍 Promise.resolve
  • 普通值原样出现在结果里,位置不变
  • 有 then 方法的对象会被当成 Promise 等待
  • 全是普通值时,结果仍是异步拿到的

普通值、Promise、thenable 混在一起

实际代码里经常出现这种情况:一部分数据有缓存,直接是值;另一部分要请求,是 Promise。放进同一个 Promise.all 就行,不用先判断类型。

看看每一项最后变成了什么JavaScript
const thenable = { then(resolve) { resolve('thenable 的值') } }

Promise.all([1, 'a', null, Promise.resolve(2), thenable]).then((res) => {
  console.log(JSON.stringify(res))
})
console.log('同步代码结束')
查看输出与解释
同步代码结束
[1,"a",null,2,"thenable 的值"]

数字、字符串、null 都原样保留了。Promise.resolve(2) 取出了兑现值 2。至于 thenable 这个对象,它本身没有出现在结果里,出现的是它通过 resolve 交出来的字符串,因为它有 then 方法,被当成 Promise 处理了。「同步代码结束」先打印,说明就算大部分是普通值,结果也不是同步给出的。 这就是为什么缓存读取的函数不用非得写成 async:返回值是普通值还是 Promise,Promise.all 都能吃下。

结果顺序为什么不受完成先后影响

Promise.all 遍历时给每一项记住它的下标,某一项兑现后,把值写进结果数组对应的位置,而不是 push 到末尾。所以结果顺序永远等于传入顺序,和谁先完成无关。

慢的放前面,结果还是在前面JavaScript
const delay = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))

// 慢的放前面,普通值放中间,快的放后面
Promise.all([delay('慢', 30), '普通值', delay('快', 10)]).then((res) => {
  console.log(res.join(' | '))
})
Promise.all(123).catch((err) => console.log(err.name))
查看输出与解释
TypeError
慢 | 普通值 | 快

「快」10 毫秒就完成了,普通值更早,但结果数组里它们仍然待在自己的位置上。这让你可以放心用解构取值:const [user, config, list] = await Promise.all([...]),不用担心对不上号。 最后一行传了一个数字 123,而 Promise.all 要的是可迭代对象,不是「数组里的每一项」必须是什么。数字不可迭代,所以返回的 Promise 以 TypeError 拒绝。注意它是拒绝,不是同步抛错,外面包 try 接不住,要用 catch。

回答前,多想一步

容易答错的地方

以为普通值会让 Promise.all 报错或被跳过
普通值既不会报错,也不会被过滤。结果数组的长度永远等于输入的长度,普通值在哪一位,结果就在哪一位。
以为全是普通值时可以同步拿到结果
Promise.all([1, 2, 3]) 返回的仍然是一个 Promise,每一项都要经过一次 then 才会写进结果,这些 then 回调在微任务里执行。所以 console.log(Promise.all([1, 2])) 打印的是 Promise 对象,不是 [1, 2]。
忘了普通对象上可能有 then
如果你放进去的数据对象恰好有一个 then 方法,比如某些链式调用的查询构造器,Promise.all 会调用它的 then 并等待,拿到的不是这个对象本身。如果它的 then 永远不调用回调,整个 Promise.all 就会一直 pending。
试着用自己的话回答

面试官还会怎么问?

结果里的对象和传进去的是同一个引用吗?

是。普通对象被 Promise.resolve 包装时不会被复制,兑现值就是原对象的引用。在结果里修改它,原来那个变量看到的也会变。

手写 Promise.all 时怎么处理普通值?

不要自己判断 instanceof Promise,直接对每一项调用 Promise.resolve(item).then(...)。这样普通值、原生 Promise、thenable 三种情况一次全覆盖,也和规范行为一致。

数组里有一个 undefined 会怎样?

和其他普通值一样,被包成兑现值为 undefined 的 Promise,结果数组对应位置就是 undefined。它不代表失败,想表示失败要让那一项 reject。

从一道题,走向一组知识

把知识连起来

异步与 Promise

Promise.all 有一个 reject 了,会立刻失败吗?剩下的任务还会跑吗?

普通值包装后永远不会失败,但数组里只要有一个真正的 Promise 拒绝,整体就会提前失败。

异步与 Promise

Promise.all 传一个空数组进去,会一直 pending 吗?

空数组连一个值都没有,Promise.all 当场就是 fulfilled,而放了普通值的数组要等一次微任务才兑现。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Promise.all()

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

本题目录
  1. 先记住这个答案
  2. 普通值、Promise、thenable 混在一起
  3. 结果顺序为什么不受完成先后影响
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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