2

In my react app I have some user ids and need to fetch their emails and names from different endpoints so what i do is:

const promises = ids.map(
   id => (  {email: axios.get(`blabla/${id}/email`), name: axios.get(`blabla/${id}/name`)}  )
);

and it gets me back:

[
{email: Promise, name: Promise},{email: Promise, name: Promise},{email: Promise, name: Promise},...
]

Now to get the data what i do is:

  const results = [];
  promises.map(({ email, name}) =>
      Promise.all([email, name]).then((result) => {
        results.push({
          email: result[0].data,
          name: result[1].data,
        });
      })
    );

but i have a feeling that it may not be a good way to it, i mean it works now but i don't want to get into any issues later !! haha, for instance, a race between promises, or for instance, a user email set for another user. I don't know if they are even possible to happen but I need to check with you experts that if you confirm this way or do you suggest something else.

2 Answers2

1

It's fine other than that you're not using the array map creates. Any time you're not using map's return value, use a loop (or forEach) instead. Also, any time you're writing to an array you're closing over from asynchronous operation results, it sets you up to accidentally use that array before it's populated

In this case, though, I'd use the array map gives back:

Promise.all(
    promises.map(({ email, name}) =>
        Promise.all([email, name])
        .then(([email, name]) => ({email, name}))
    )
).then(results => {
    // ...use results here, it's an array of {email, name} objects
})
.catch(error => {
    // ...handle error here, at least one operation failed...
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • thanks indeed for your answer, could you please explain a bit more about what you said: `any time you're writing to an array you're closing over from asynchronous operation results, it sets you up to accidentally use that array before it's populated` – react developer Jun 16 '20 at 17:22
  • @reactdeveloper - With the code in your question, if you used `results` in code immediately after the `map` call, it wouldn't have anything in it. More here: https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron – T.J. Crowder Jun 17 '20 at 07:20
1

I'd be doing it like this.

const promises = ids.map(
   id => (  [axios.get(`blabla/${id}/email`), axios.get(`blabla/${id}/name`)]  )
);
Promise.all(promises.map(Promise.all).then(
    ([email, name]) => {
        console.log(email, name)
    }
)).then(values => {
    console.log(values);
});
Siraj Alam
  • 9,217
  • 9
  • 53
  • 65
  • thanks for your answer, could you explain it a bit more, for instance, this part: promises.map(Promise.all) – react developer Jun 16 '20 at 19:18
  • the map takes a callback and passes each entry on each interaction, so I passed Promise. All as the callback, where it takes an argument which is another array [email, name] and resolve it and return it to the map. It is the same as the answer above, except he defined the function then called Promise and I just passed the promise function directly. – Siraj Alam Jun 16 '20 at 19:22