Promise.all:已解决的值的顺序

Tho*_*isé 163 javascript promise es6-promise

看看MDN,看起来values传递给then()Promise.all 的回调包含了promises顺序的值.例如:

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?
});
Run Code Online (Sandbox Code Playgroud)

任何人都可以引用规范说明values应该在哪个顺序?

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

Nit*_*Nit 230

不久,订单被保留.

下面您链接到规范,Promise.all(iterable)需要一个iterable(即,支持对象Iterator接口)作为参数和以后的调用PerformPromiseAll( iterator, constructor, resultCapability)它,其中超过后者循环iterable使用IteratorStep(iterator).
这意味着如果您传入的可迭代内容Promise.all()是严格排序的,那么一旦传入它们仍然会被订购.

通过Promise.all() Resolve每个已解析的promise具有内部[[Index]]槽来实现解析,该内部槽标记原始输入中的promise的索引.


所有这些意味着输出严格按输入进行排序,只要输入是严格排序的(例如,数组).

你可以在下面的小提琴(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));
});
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,承诺不会链接.虽然您将以相同的顺序获得解决方案,但无法保证承诺何时被执行.换句话说,`Promise.all`不能用于按顺序依次运行一系列promise.加载到迭代器中的promise需要彼此独立才能实现可预测的工作. (8认同)
  • @BenjaminGruenbaum在迭代两次时,是否有可能产生一个迭代产生两个不同的顺序?例如,在迭代时以随机顺序生成卡片的一副牌?我不知道"严格排序"是否是正确的术语,但并非所有迭代都有固定的顺序.因此我认为_iterators_是"严格排序"(假设这是正确的术语)是合理的,但_iterables_不是. (3认同)
  • @JLRishe我猜你是对的,它确实是有序的迭代器 - 迭代不是. (3认同)
  • 一个可迭代对象如何不被严格排序?任何可迭代对象都按照它产生其值的顺序“严格排序”。 (2认同)

Ben*_*uer 33

正如先前的答案已经陈述的那样,Promise.all使用与原始Promises的输入顺序相对应的数组聚合所有已解析的值(请参阅聚合承诺).

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

对于开发人员来说,看起来Promise是按顺序完成的,但实际上,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)
Run Code Online (Sandbox Code Playgroud)

在上面显示的代码中,给出了三个Promises(A,B,C)Promise.all.三个Promise以不同的速度执行(C是最快的,B是最慢的).这就是Promise的console.log陈述按此顺序出现的原因:

C (fast) 
A (slow)
B (slower)
Run Code Online (Sandbox Code Playgroud)

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

['A (slow)', 'B (slower)', 'C (fast)']
Run Code Online (Sandbox Code Playgroud)

如果您还想保证Promise的实际执行,那么您需要一个像Promise队列这样的概念.这是一个使用p-queue的例子(小心,你需要在函数中包装所有的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);
Run Code Online (Sandbox Code Playgroud)

结果

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

['A (slow)', 'B (slower)', 'C (fast)']
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,特别是使用 PQueue (5认同)
  • 这和 for-of-loop 以及按顺序等待每个 Promise 之间有什么区别? (2认同)

Ber*_*rgi 26

是的,值results与的顺序相同promises.

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

  • @RoyiNamir:显然他是。 (2认同)