Promise.all:已解析值的顺序

IT技术 javascript promise es6-promise
2021-01-23 17:17:36

查看MDN,它看起来像values传递给then()Promise.all回调包含按照Promise顺序的值。例如:

var somePromises = [1, 2, 3, 4, 5].map(Promise.resolve);
return Promise.all(somePromises).then(function(results) {
  console.log(results) //  is [1, 2, 3, 4, 5] the guaranteed result?
});

任何人都可以引用说明values应该按哪个顺序排列的规范吗?

PS:运行这样的代码表明这似乎是真的,尽管这当然没有证据 - 这可能是巧合。

3个回答

很快,订单就被保留了下来

按照您链接的规范,Promise.all(iterable)将 aniterable作为参数并在内部调用PerformPromiseAll(iterator, constructor, resultCapability)它,后者iterable使用IteratorStep(iterator).

解析是通过Promise.all() Resolve每个解析的Promise都有一个内部[[Index]]插槽来实现的,它标记了原始输入中Promise的索引。


所有这一切意味着输出是严格排序的,因为您传递给 Promise.all() 的迭代是严格排序的(例如,一个数组)。

您可以在下面的小提琴(ES6)中看到这一点:

// Used to display results
const write = msg => {
  document.body.appendChild(document.createElement('div')).innerHTML = msg;
};

// Different speed async operations
const slow = new Promise(resolve => {
  setTimeout(resolve, 200, 'slow');
});
const instant = 'instant';
const quick = new Promise(resolve => {
  setTimeout(resolve, 50, 'quick');
});

// The order is preserved regardless of what resolved first
Promise.all([slow, instant, quick]).then(responses => {
  responses.map(response => write(response));
});

值得注意的是,这些Promise并不连锁。虽然您会以相同的顺序获得解决方案,但无法保证Promise何时执行。换句话说,Promise.all不能用于按顺序运行一系列Promise,一个接一个。加载到迭代器中的 Promise 需要相互独立,才能以可预测的方式工作。
2021-03-11 17:17:36
注意 - Firefox 是唯一在 promises 中正确实现迭代的浏览器。Chrome浏览器将当前throw的错误时抛出,如果你传递一个迭代来Promise.all此外,我不知道当前支持传递可迭代对象的任何用户空间Promise实现,尽管当时许多人对此进行了辩论并决定反对它。
2021-03-21 17:17:36
@JLRishe 我想你是对的,它确实是有序的迭代器 - 迭代器不是。
2021-03-27 17:17:36
一个可迭代对象如何不被严格排序?任何可迭代对象都按照其产生值的顺序“严格排序”。
2021-03-30 17:17:36
@BenjaminGruenbaum 是否有可能在迭代两次时产生两个不同顺序的迭代?例如,一副纸牌在迭代时以随机顺序产生卡片?我不知道“严格排序”是否是这里的正确术语,但并非所有可迭代对象都有固定顺序。所以我认为可以合理地说迭代器是“严格有序的”(假设这是正确的术语),但迭代器不是。
2021-03-31 17:17:36

正如前面的答案已经指出的那样,Promise.all将所有已解析的值与与原始 Promise 的输入顺序相对应的数组聚合(请参阅聚合 Promises)。

但是,我想指出的是,该订单仅保留在客户端!

对于开发人员来说,看起来 Promises 是按顺序完成的,但实际上,Promises 以不同的速度处理。当您使用远程后端时,了解这一点很重要,因为后端可能会以不同的顺序接收您的 Promise。

以下是通过使用超时来演示该问题的示例:

Promise.all

const myPromises = [
  new Promise((resolve) => setTimeout(() => {resolve('A (slow)'); console.log('A (slow)')}, 1000)),
  new Promise((resolve) => setTimeout(() => {resolve('B (slower)'); console.log('B (slower)')}, 2000)),
  new Promise((resolve) => setTimeout(() => {resolve('C (fast)'); console.log('C (fast)')}, 10))
];

Promise.all(myPromises).then(console.log)

在上面显示的代码中,三个Promise(A、B、C)被赋予了Promise.all这三个 Promise 以不同的速度执行(C 最快,B 最慢)。这就是 Promise 的console.log语句按以下顺序显示的原因

C (fast) 
A (slow)
B (slower)

如果 Promise 是 AJAX 调用,则远程后端将按此顺序接收这些值。但是在客户端Promise.all确保根据myPromises数组的原始位置对结果进行排序这就是为什么最终结果是:

['A (slow)', 'B (slower)', 'C (fast)']

如果您还想保证 Promise 的实际执行,那么您将需要一个类似 Promise 队列的概念。下面是一个使用p-queue的例子(注意,你需要把所有的 Promise 都包装在函数中):

顺序Promise队列

const PQueue = require('p-queue');
const queue = new PQueue({concurrency: 1});

// Thunked Promises:
const myPromises = [
  () => new Promise((resolve) => setTimeout(() => {
    resolve('A (slow)');
    console.log('A (slow)');
  }, 1000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('B (slower)');
    console.log('B (slower)');
  }, 2000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('C (fast)');
    console.log('C (fast)');
  }, 10))
];

queue.addAll(myPromises).then(console.log);

结果

A (slow)
B (slower)
C (fast)

['A (slow)', 'B (slower)', 'C (fast)']
我需要一个 Sequential Promise Queue,但是如果我必须从结果 sql 记录中执行它怎么办?在一个为?而 ?,在 ES2017 和 ES2018 中别无选择?
2021-03-18 17:17:36
很好的答案,特别是使用 PQueue
2021-03-20 17:17:36
PQueue 帮了我!谢谢!:)
2021-03-26 17:17:36
这个和 for-of-loop 有什么区别,按顺序等待每个Promise?
2021-04-06 17:17:36

是的,中的值resultspromises.

人们可能会在 上引用ES6 规范Promise.all,但由于使用了迭代器 api 和通用Promise构造函数,这有点令人费解。但是,您会注意到每个解析器回调都有一个[[index]]在 promise-array 迭代中创建属性,用于设置结果数组上的值。

@厄齐尔沃特?当所有Promise都实现时,解决的时间顺序绝对无关紧要。结果数组中值的顺序与Promise的输入数组中的顺序相同。如果不是,您应该切换到正确的Promise实现。
2021-03-16 17:17:36
@RoyiNamir:显然他是。
2021-03-27 17:17:36
奇怪,我今天看到一个youtube视频说输出顺序由第一个解决的人决定,然后第二个,然后.....我猜视频OP错了?
2021-04-05 17:17:36