How do I convert a JS promise to another object

1.3k views Asked by At

So I have an asynchronous function that I need to call a number of times. Naturally, I would like to use Promise.all

const promises = [];
ids.forEach((id) => {
  stockoutPromises.push(
    asyncFunc(id),
  });
});
results[] = Promise.all(promises);

Okay no problem there but how do I know which id goes with which result? Sure I could iterate through both arrays again, but is there another way to do this? Is there a way that I can create a promise that when resolved gives me an object that contains both id and result?

Thanks!

3

There are 3 answers

0
CertainPerformance On

Chain a .then onto the call of asyncFunc so that the resulting item is not just of the asyncFunc result, but of it and the ID in an object:

ids.forEach((id) => {
  stockoutPromises.push(
    asyncFunc(id).then(result => ({ id, result }))
  );
});

But it'd be better to use .map instead of .push in a loop:

const results = await Promise.all(
  ids.map(id =>
    asyncFunc(id).then(result => ({ id, result }))
  )
);
const firstItem = results[0];
console.log(firstItem.result, firstItem.id);
0
dave On

The result of Promise.all will be in the order that they were in originally (in the array of promises). So you can easily reassociate just using the index:

// zip takes an array of keys, and an array of values, and creates an object:
const zip = (a, b) => Object.fromEntries(a.map((k, i) => [k, b[i]]));

// first id will resolve last, second will resolve next, etc.
const ids = [0, 1, 2, 3, 4];
const promises = ids.map((i) => new Promise((resolve) => {
    setTimeout(() => resolve(i), 700 - (100 * i));
}));

// but we still get the correct item in each spot, because of how
// Promise.all works:
(async () => {
    console.log(zip(ids, await Promise.all(promises)));
})()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all:

Returned values will be in order of the Promises passed, regardless of completion order.

0
nehtron On

Instead of using forEach, use map:

const promises = ids.map((id) => {
    return asyncFunc(id)
});
const results = await Promise.all(promises);

map will return a new array of objects based on what the supplied function returns. In your case, you are calling an asyncFunc, which, I assume, returns a promise itself. So you can just return the results of map directlty without pushing to a new promises array.

Also, make sure to "await" the call to Promise.all;

You can check it out here in this jsfiddle that simply has the asyncFunc return a promise that doubles the id in the ids array.