将“ map”与promise一起使用可返回有序和无序结果

时间:2018-11-30 23:28:54

标签: javascript arrays node.js promise es6-promise

我有这段代码:

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.logmap内,打印列表无序

4 个答案:

答案 0 :(得分:3)

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

但是,如果您在诺言得到解决后立即输出值 ,那么上述内容当然对您的输出没有影响-现在,将按照个人诺言解决的顺序进行排序。

简单示例:

让我们说有三个承诺p1p2p3可以分解为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函数,它们将被排序。