将"map"与承诺一起使用可返回有序和无序结果



我有一段代码:

const Axios = require('axios');
const baseURL = 'https://jsonplaceholder.typicode.com';
async function main() {
const posts = await Axios(`${baseURL}/posts`);
const users = await Promise.all(posts.data.map( async (post, index) => {
let d = await Axios(`${baseURL}/users/${post.userId}`);
return d.data.name;
}))
users.map( (user, index) => {
console.log( `${index}. ${user}` );
});
}

并按顺序输出结果:

1. Patricia
2. Glenna
3. Nicholas
4. Kurtis
5. Chelsey

一切都好,但是。。。如果我这样做:

async function main() {
const posts = await Axios(`${baseURL}/posts`);
await Promise.all(posts.data.map( async (post, index) => {
let d = await Axios(`${baseURL}/users/${post.userId}`);
console.log( `${index}. ${d.data.name}` );
}))
}

CCD_ 2内部的CCD_。。。

2. Glenna
4. Kurtis
5. Chelsey
1. Patricia
3. Nicholas

我的问题是:

为什么在第一个代码map中返回列表有序,而在第二个代码console.logmap中打印列表无序

Promise.all旨在保持传递给它的承诺结果的顺序,而与这些承诺实际解决的顺序无关。因此,当Promise.all解析时,它意味着所有单独的promise都已解析,然后Promise.all解析为分辨率的数组,其顺序与数组中相应promise的顺序相同。

但是,如果您在值的promise解析后立即输出,那么上述内容当然不会对您的输出产生影响——它现在将按照单个promise解析的顺序进行排序。

简单示例:

假设有三个承诺p1console.log0和p3,它们解析为1、2和3。但第二个承诺会比其他两个更快地得到解决。

用CCD_ 13调用CCD_。它返回一个新的承诺,该承诺最终将解析为[1, 2, 3]。在并非所有承诺都得到解决的过程中,您可以想象一个内部数组的演变如下:

  • p2解析。Promise.all内部存储[undefined, 2, undefined]
  • CCD_ 18解析。Promise.all内部存储map0
  • CCD_ 21解析。Promise.all也解析为值[1, 2, 3]

在这里,您可以看到第一个代码将输出2,1,3,而第二个将输出1,2,3

因为如果使用异步代码,那么"激发"请求的顺序无关紧要,它只计算响应所需的时间。

因此,您的结果将按照您的请求完成的方式排序,因此,如果您对x的请求首先完成,即使您最后一次发射,它也将位于您的结果的第一个位置

映射功能是"阻塞",这意味着在第一个请求完成后触发第二个请求,依此类推

这里是一个例子:https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/

当您使用Promise.all(请求(时,所有请求都是并行进行的,因此您无法知道哪个请求在另一个请求之前结束。

在第一段代码中,您已经按照请求数组的顺序得到了结果。但在第二个例子中,console.log是按照响应的顺序排列的。

因为Promise.all并行执行promise,并且是异步的,而.map是阻塞的,并且按顺序执行,直到迭代完所有项才结束。就像每个循环一样。

如果你想通过订购来实现,我建议你使用Bluebird.each(库(或类似的东西:

const promiseEach = promises => {
const results = [];
return promises
.reduce((acc, val, idx) => acc.then(_ => ((idx > 0 && results.push(_)), val)), Promise.resolve())
.then(_ => [...results, _]);
}
const a1 = Promise.resolve(1);
const a2 = Promise.resolve(2);
const a3 = Promise.resolve(3);
const d1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 3000); }); // resolves after 3 seconds.
const d2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 2000); }); // resolves after 2 seconds.
const d3 = new Promise((resolve, reject) => { setTimeout(() => resolve(3), 1000); }); // resolves after 1 seconds.
// this will respect orderings, before first promise is not resolved, does not goes to next one.
promiseEach([a1, a2, a3, d1, d2, d3]).then(console.log);

并将您的地图传递给promiseEach函数,它们将被订购。