我有这段代码:
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}` );
}))
}
console.log
中的map
打印列表无序...
2. Glenna
4. Kurtis
5. Chelsey
1. Patricia
3. Nicholas
我的问题是:
为什么在第一个代码 map
中返回列表已排序,而在第二个代码中返回console.log
在map
内,打印列表无序?
答案 0 :(得分:3)
Promise.all
旨在保持传递给它的承诺结果的顺序,而与这些承诺实际解决的顺序无关。因此,Promise.all
解决时,意味着所有诺言都已解决,然后Promise.all
解析为决议数组,其顺序与相应的诺言中的承诺相同。 / p>
但是,如果您在诺言得到解决后立即输出值 ,那么上述内容当然对您的输出没有影响-现在,将按照个人诺言解决的顺序进行排序。
让我们说有三个承诺p1
,p2
,p3
可以分解为1、2和3。但是第二个承诺要比其他两个要早解决。
Promise.all
与[p1, p2, p3]
一起调用。它返回一个新的承诺,最终将解决为[1, 2, 3]
。在不是所有的诺言都得到解决的时间内,您可以想象内部数组的演变如下:
p2
解决。 Promise.all
在内部存储[undefined, 2, undefined]
p1
解决。 Promise.all
在内部存储[1, 2, undefined]
p3
解决。 Promise.all
的解析度也为[1, 2, 3]
在这里您可以看到第一个代码将输出2、1、3,而第二个代码将输出1、2、3
答案 1 :(得分:1)
因为如果您使用异步代码,则“触发”请求的顺序无关紧要,因此只计算响应需要多长时间。
因此,您的结果将按照请求的完成顺序进行排序,因此,如果您对x的请求先完成,即使您最后触发了它,它也将位于结果的第一位置
map函数是“ blocking”(阻塞),这意味着第二个请求在第一个请求完成后被触发,依此类推。
这里是一个示例:https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/
答案 2 :(得分:1)
使用Promise.all(requests)时,所有请求都是并行进行的,因此您无法知道哪个请求在另一个请求之前结束。
在第一个代码中,您已经按照请求数组的顺序获得了结果。但是在第二个中,console.log按响应顺序排列。
答案 3 :(得分:1)
因为Promise.all
并行执行promise并且是异步的,而.map
处于阻塞状态并按顺序执行,并且直到对所有项目进行迭代时它才会结束。就像for-each循环。
如果您想通过订购来实现,我建议您使用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
函数,它们将被排序。