Promise.all:已解析值的顺序

2022-08-30 00:17:56

看看MDN,看起来传递给Promise.all的回调包含了按承诺顺序排列的值。例如:valuesthen()

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:像这样运行代码表明这似乎是真的,尽管这当然不是证据 - 这可能是巧合。


答案 1

很快,订单被保留

按照您链接到的规范,Promise.all(iterable)可迭代对象作为参数,并在内部调用PerformPromiseAll(迭代器,构造函数,resultCapability),后者使用IteratorStep(迭代器)循环使用。iterable

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


所有这些都意味着输出是严格排序的,给定传递给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));
});

答案 2

如前面的答案所述,使用与原始 Promise 的输入顺序相对应的数组聚合所有已解析的值(请参见聚合 Promises)。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)

在上面显示的代码中,三个承诺(A,B,C)被赋予。三个承诺以不同的速度执行(C是最快的,B是最慢的)。這就是為什麼應許的陳述按以下順序顯示:Promise.allconsole.log

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

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

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

如果你想保证你的承诺的实际执行,那么你需要一个像承诺队列这样的概念。下面是一个使用 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);

结果

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

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