Promise.all:解析值的顺序
Posted
技术标签:
【中文标题】Promise.all:解析值的顺序【英文标题】:Promise.all: Order of resolved values 【发布时间】:2015-03-19 22:13:16 【问题描述】:查看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:运行这样的代码表明这似乎是真的,尽管这当然不能证明——这可能是巧合。
【问题讨论】:
【参考方案1】:很快,the order is preserved。
按照您链接的规范,Promise.all(iterable)
将iterable
作为参数并在内部调用PerformPromiseAll(iterator, constructor, resultCapability)
,后者使用IteratorStep(iterator)
循环iterable
。
解析是通过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));
);
【讨论】:
如何不严格排序可迭代对象?任何可迭代对象都按照其产生值的顺序“严格排序”。 注意 - Firefox 是唯一在 Promise 中正确实现迭代的浏览器。如果您将一个可迭代对象传递给Promise.all
,Chrome 目前将向throw
发送一个异常。此外,我不知道目前有任何支持传递可迭代对象的用户态承诺实现,尽管当时许多人对此进行了辩论并决定反对它。
@BenjaminGruenbaum 是否有可能在迭代两次后产生两个不同的订单?例如,一副牌在迭代时以随机顺序产生牌?我不知道“严格排序”是否是正确的术语,但并非所有迭代都有固定的顺序。所以我认为说 iterators 是“严格排序的”(假设这是正确的术语)是合理的,但 iterables 不是。
@JLRishe 我想你是对的,确实是迭代器是有序的——迭代器不是。
值得注意的是,promise 没有链接。虽然您将以相同的顺序获得解决方案,但无法保证何时执行承诺。换句话说,Promise.all
不能用于依次运行一组 Promise,一个接一个。加载到迭代器中的 Promise 需要彼此独立才能使其工作可预测。【参考方案2】:
正如前面的答案已经说明的那样,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(A、B、C)被赋予了Promise.all
。三个 Promise 以不同的速度执行(C 是最快的,B 是最慢的)。这就是为什么 Promise 的 console.log
语句按此顺序显示的原因:
C (fast)
A (slow)
B (slower)
如果 Promises 是 AJAX 调用,那么远程后端将按此顺序接收这些值。但在客户端Promise.all
确保结果按照myPromises
数组的原始位置排序。这就是为什么最终的结果是:
['A (slow)', 'B (slower)', 'C (fast)']
如果您还想保证 Promise 的实际执行,那么您需要一个类似于 Promise 队列的概念。这是一个使用p-queue 的示例(注意,您需要将所有 Promises 包装在函数中):
顺序承诺队列
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)']
【讨论】:
很好的答案,特别是使用 PQueue 我需要一个顺序承诺队列,但是如果我必须从结果 sql 记录中执行它怎么办?在一个为?虽然?,在 ES2017 我们的 ES2018 中别无选择? PQueue 帮助了我!谢谢! :) this 和 for-of-loop with await each promise in order 有什么区别?【参考方案3】:是的,results
中的值与promises
的顺序相同。
有人可能会引用ES6 spec on Promise.all
,尽管由于使用了迭代器 api 和通用 Promise 构造函数,它有点令人费解。但是,您会注意到每个解析器回调都有一个 [[index]]
属性,该属性是在 promise-array 迭代中创建的,用于设置结果数组的值。
【讨论】:
奇怪,我今天看到一个youtube视频说输出顺序是由第一个解决的,然后第二个,然后......我猜视频OP错了? @RoyiNamir:显然他是。 @Ozil Wat?当所有承诺都实现时,解决的时间顺序绝对无关紧要。结果数组中值的顺序与输入的承诺数组中的顺序相同。如果不是,您应该切换到正确的 Promise 实现。以上是关于Promise.all:解析值的顺序的主要内容,如果未能解决你的问题,请参考以下文章
与 Promise.all() 中的解析相比,为啥在 while 循环中单独解析 Promise 数组时解析更慢? [复制]
JavaScript Promise.all - 如何检查解析状态?