我有一段代码:
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.log
在map
中打印列表无序?
Promise.all
旨在保持传递给它的承诺结果的顺序,而与这些承诺实际解决的顺序无关。因此,当Promise.all
解析时,它意味着所有单独的promise都已解析,然后Promise.all
解析为分辨率的数组,其顺序与数组中相应promise的顺序相同。
但是,如果您在值的promise解析后立即输出,那么上述内容当然不会对您的输出产生影响——它现在将按照单个promise解析的顺序进行排序。
简单示例:
假设有三个承诺p1
、console.log
0和p3
,它们解析为1、2和3。但第二个承诺会比其他两个更快地得到解决。
用CCD_ 13调用CCD_。它返回一个新的承诺,该承诺最终将解析为[1, 2, 3]
。在并非所有承诺都得到解决的过程中,您可以想象一个内部数组的演变如下:
p2
解析。Promise.all
内部存储[undefined, 2, undefined]
- CCD_ 18解析。
Promise.all
内部存储map
0 - 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
函数,它们将被订购。