的承诺。all:解析值的顺序

查看中数,它看起来像传递给Promise的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:运行这样的代码表明这似乎是正确的,尽管这当然没有证据-这可能是巧合。

131665 次浏览

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

有人可能会引用ES6规范在Promise.all,尽管由于使用了迭代器api和泛型承诺构造函数,它有点令人费解。然而,你会注意到每个解析器回调都有一个[[index]]属性,该属性是在承诺数组迭代中创建的,用于设置结果数组上的值。

不久,顺序被保留

根据你链接到的规范,Promise.all(iterable)接受iterable作为参数,并在内部用它调用PerformPromiseAll(iterator, constructor, resultCapability),后者使用IteratorStep(iterator)iterable上循环。

解析是通过Promise.all() Resolve实现的,其中每个已解析的承诺都有一个内部[[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));
});

正如前面的答案所述,Promise.all将所有已解析的值与原始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 (A, B, C)被赋予Promise.all。三个承诺以不同的速度执行(C是最快的,B是最慢的)。这就是为什么应许的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)']