Promise.all:解析值的顺序

Promise.all: Order of resolved values

查看 MDN 似乎传递给 Promise.all 的 then() 回调的 values 包含按承诺顺序排列的值。例如:

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

是的,results 中的值与 promises 中的值顺序相同。

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

很快,the order is preserved

根据您链接到的规范,Promise.all(iterable) takes an iterable as a parameter and internally calls PerformPromiseAll(iterator, constructor, resultCapability) with it, where the latter loops over iterable using IteratorStep(iterator)

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


这意味着输出是严格排序的,因为您传递给 Promise.all() 的可迭代对象是严格排序的(例如,数组)。

您可以在下面看到这个动作 fiddle (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.all 将所有已解析的值与对应于原始 Promises 的输入顺序的数组聚合在一起(参见 Aggregating Promises)。

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

对于开发人员来说,Promise 看起来是按顺序完成的,但实际上,Promise 的处理速度不同。当您使用远程后端时,了解这一点很重要,因为后端可能会以不同的顺序接收您的 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.all 提供了三个 Promise(A、B、C)。三个 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 包装在函数中):

顺序承诺队列

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)']