Map Promise.all output with promises index

2019-05-26 21:24发布

问题:

I am using nodejs v8+ which supports default async await style of code.

In my problem, I am trying to push all the promises into an array and then use Promise.all to with await keyword to get the responses. But the problem is, I am not able to map the promises with the keys.

Here is the example:

let users = ["user1", "user2", "user3"];

    let promises = [];
    for(let i = 0; i < users.length; i++){
      let response = this.myApiHelper.getUsersData(users[i]);
      promises.push(response);
    }
    let allResponses = await Promise.all(promises);

Here I get all the collected response of all the responses, but I actually want to map this by user. For example currently I am getting data in this format:

[
{
  user1 Data from promise 1
},
{
  user2 Data from promise 2
},
{
  user3 Data from promise 3
}
]

But I want data in this format:

[
 {
  "user1": Data from promise 1
 },
 {
   "user2": Data from promise 2
 },
 {
   "user3": Data from promise 3
 }
]

I am sure there must be a way to map every promise by user, but I am not aware of.

回答1:

We gonna create an array of user. Iterate over it to create an array of Promise we give to Promise.all. Then iterate on the answer to create an object that's matching the user with the associated answer from getUsersData.

Something to know about Promise.all is that the order of the returned data depends on the order of the promises given in entry of it.


const users = ['user1', 'user2', 'user3'];

const rets = await Promise.all(users.map(x => this.myApiHelper.getUsersData(x)));

const retWithUser = rets.map((x, xi) => ({
   user: users[xi],
   ret: x,
}));

Here you have a great tutorial about Array methods (map, filter, some...).



回答2:

Though the answer accepted by me is one of the solution, which I think is perfect and I will keep as accepted answer but I would like to post my solution which I figured out later which is much simpler:

We could simply use this:

let finalData = {};
let users = ["user1", "user2", "user3"];
await Promise.all(users.map(async(eachUser) => {
    finalData[user] = await this.myApiHelper.getUsersData(eachUser);
}))
console.log(finalData);


回答3:

as described here How to use Promise.all with an object as input

Here is a simple ES2015 function that takes an object with properties that might be promises and returns a promise of that object with resolved properties.

function promisedProperties(object) {

  let promisedProperties = [];
  const objectKeys = Object.keys(object);

  objectKeys.forEach((key) => promisedProperties.push(object[key]));

  return Promise.all(promisedProperties)
    .then((resolvedValues) => {
      return resolvedValues.reduce((resolvedObject, property, index) => {
        resolvedObject[objectKeys[index]] = property;
        return resolvedObject;
      }, object);
    });

}

And then you will use it like so:

var promisesObject = {};
users.map((element, index) => object[element] = this.myApiHelper.getUsersData(users[index]));
promisedProperties(object).then(response => console.log(response));

Note that first of all you need an object with key/promise.