Promise.all([p1, p2]) 只能靠索引拿结果,这事儿忍了十年——Firefox 155 今天把它彻底原生化了

用过 Promise.all 的都知道,结果是个数组,顺序和你传入的顺序严格对应。

想象一个常见的真实场景:你同时请求用户信息、订单列表、商品推荐三个接口,写出来是这样的:

const [user, orders, items] = await Promise.all([
  fetchUser(),
  fetchOrders(),
  fetchItems()
]);

看起来没问题。但三个月后你重构这段代码,加了一个接口、调了顺序,忘记同步改解构赋值——程序不会报错,user 拿到的是 orders 的数据,orders 拿到的是 items 的。线上开始出现莫名其妙的用户信息错乱。

这个坑在团队代码库里存在了十年,因为 Promise.all 本身从来不告诉你哪个位置对应哪个 promise。

Firefox 155 稳定版今天把这个彻底翻了。

新增的 Promise.allKeyed()Promise.allSettledKeyed(),接受一个对象而不是数组:

const { user, orders, items } = await Promise.allKeyed({
  user: fetchUser(),
  orders: fetchOrders(),
  items: fetchItems()
});
// user 就是用户信息,orders 就是订单,items 就是商品推荐
// 顺序怎么调,结果都能对应上

Promise.allSettledKeyed() 同理,但返回结构是 { status: 'fulfilled'|'rejected', value|reason }

const results = await Promise.allSettledKeyed({
  user: fetchUser(),
  orders: fetchOrders(),
  items: fetchItems()
});

// 哪个失败了,一目了然
for (const [key, result] of Object.entries(results)) {
  if (result.status === 'rejected') {
    console.error(`${key} failed:`, result.reason);
  }
}

为什么说它是彻底原生化而不是简单封装?

这是 TC39「await dictionary」提案的标准实现,Firefox 155 是第一个稳定支持的浏览器。你不需要任何 polyfill,不需要任何库,结果靠名字拿而不是靠索引猜——这才是 Promise 组合子该有的样子。

Safari 还没跟上来,但 Firefox 155 已经稳定了这两个方法,Chrome 预计下个版本也会支持。

下一步怎么做?

如果你现在有类似 Promise.all([p1, p2, p3]) 的代码,先数一数数组里每个 promise 是什么,然后:

  1. 打开 Firefox 155,检查是否支持 Promise.allKeyed
  2. 把数组改成对象字面量,key 就是你想叫的名字
  3. 把解构改成对象属性访问 results.user 而不是 results[0]

这不只是一个写法上的便利——它把运行时才会暴露的顺序 bug,提前到写代码的时候就能发现。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 15 阅读