0

this is bit more theoretical question. I originally intented to call this question Is it possible to iterate over map twice, but just from the sound of it, it sounds like an anti-pattern. So I assume I'm just approaching this wrong.

Also note: Data here servers as an abstraction. I know that what I'm doing here with data provided here is unnecessary, but please don't get fixated too much on data-structure and what-not. It does not represent the real (more complex, which furthermore is provided by client and I can't alter) data I'm working with. Instead approach the problem as how to return structured async calls for each array item please! :-)


My problem boils down to this:

  • I have array of ids on which I need to execture two separate asynchronous calls
  • Both of these callls need to pass (and in all id instances)

So as an example, imagine I have these two data-sets:

const dataMessages = [
  { id: "a", value: "hello" }, 
  { id: "b", value: "world" }
];

const dataNames = [
  { id: "a", name: "Samuel" },
  { id: "b", name: "John" },
  { id: "c", name: "Gustav"},
];

And an API-call mock-up:

const fetchFor = async (collection: Object[], id: string): Promise<Object> => {
  const user = collection.find(user => user.id === id);
  if (!user) {
    throw Error(`User ${id} not found`);
  }
  return user;
};

Now I need to call the fetchFor() function for both the data-sets, presumably inside the inside the Promise.all, given forEach is not asynchronous from a predetermined array of ids.

I was thinking something akin to maping a list of Promises for the Promise.all to execute. This works fine, when you only need to map a single api-call:

const run = async () => {
  const result = await Promise.all(
    ['a', 'b'].map(id => fetchFor(dataMessages, id)
  )
  console.log(result) // [{id: 'a', value: 'hello'}, {id: 'b', value: 'world}]
}

However I somehow need to return both promises for the

  • fetchFor(dataMessages, id)
  • fetchFor(dataNames, id)

inside the Promise.all array of Promises.


I guess I could always simply do a flatMap of two maps for both instances of API calls, but that sounds kinda dumb, given

  • I'd be doing array.map on same array twice
  • My data structure would not be logically connected (two separate array items for the same user, which would not even by followed by eachother)

So ideally I'd like to return dat in form of

 const result = await Promise.all([...])
 console.log(result)
 /* [
 *   {id: 'a', message: 'hello', name: 'Samuel'},
 *   {id: 'b', message: 'world', name: 'John'},
 *  ]

Or do I simply have to do flatmap of promises and then do data-merging to objects based on id identifier inside a separate handler on the resolved Promise.all?

I've provided a working example of the single-api-call mockup here, so you don't have to copy-paste.

Edit zen-wildflower-480ig

What would be the correct / common way of approaching such an issue?

2
  • Why would you need to call fetchFor(dataMessages, id)? Don't you just need to add the messages for id to the user (if there's one)? Commented Jun 12, 2020 at 15:26
  • @Andreas No. I know in the example it would make sense, but instead of messages / id please think of these as data fields inside an actual database (and obviously in reality I'm returning more than one field). I just chose those two things as an abstraction of the issue, as an representation of (much more complex) thing I have to do. Question is about returning two structured Promises inside a single map, so don't get fixated too much on the correctness of data-structure and what-not please :-) Commented Jun 12, 2020 at 15:28

1 Answer 1

2

You could nest Promise.all calls:

const [messages, names] = await Promise.all([
  Promise.all(
    ['a', 'b'].map(id => fetchFor(dataMessages, id)
  ),
  Promise.all(
    ['a', 'b', 'c'].map(id => fetchFor(dataNames, id)
  )
]);

If you're wanting to then merge the results after retrieved, it's just a matter of standard data manipulation.

Sign up to request clarification or add additional context in comments.

2 Comments

Oh yeah, now that I look back at it, i'm kinda baffled how it didn't occur to me to simply think of Promise.all as to two Promises which can be elements for argument array of another Promise.all, neat :-) !
One last thing, after bit of my own research, I've found you can also use for of with two async calls, but if I understand correctly, Promise.all approach is better, since it can run concurrently, hence faster with large set of data, right?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.