Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have concurrent async calls in a nested map function

I have an object, that has a nested array of objects, which has another nested array of objects. I have an asynchronous function that checks if they are valid. I want it to run concurrently, but I want to wait on all of the promises to return. Right now I have:

function validate(userId): Promise<User> {
  let user = await this.userRepo.findById(input).catch(err => this.handleNotExistent(err))
  let friends = user.friends || []
  await Promise.all(friends.map(async friend => {
    let validFriend = await this.friendRepo.findById(friend.id).catch(err => this.handleNotExistent(err))
    if (validFriend.name != friend.name || validFriend.age != friend.age) {
      this.handleInvalidRequest()
    }
    else {
      let friendOfFriends = friend.friendOfFriends || []
      return await Promise.all(friendOfFriends.map(async friendOfFriend => {
        let validFOF = await this.FOFRepo.findById(friendOfFriend.id).catch(err => this.handleNotExistent(err))
        if (validFOF.name != friendOfFriend.name) {
          this.handleInvalidRequest()
        }
        else {
         return validFOF
        }   
     }
   }
}


How can I rewrite this, so that it runs in order (you need to find validFriend first before you can look up their friendOfFriend, but all of the mapped items run concurrently?

like image 432
Vikram Khemlani Avatar asked Sep 18 '25 09:09

Vikram Khemlani


1 Answers

You have two problems:

  1. Nested structure to unpack
  2. How to wait on multiple promises

It would have been ideal if you could have made the code standalone and runnable on the TypeScript playground (obviously, you'd have to make this a simplified version of the real thing), because then it'd be easier to show you unwinding it.

But in spirit, the first approach I'd take would be like:

async function validate(userId): Promise<User> {
  ...
  let friends = await findFriendsAndTheirFriends(userId)
  return Promise.all(friends.map(validateFriend))
}

The first function, findFriendsAndTheirFriends() (you'd have to write that, of course), takes a given user ID and returns a list of friends and their friends. The second function takes each friend userId and returns a promise that will validate that friend.

But I don't think the way you've got exactly two levels of friends is good. Aside from being hard to read, it's very inflexible, and near impossible to debug.

You'd be better off turning it into some kind of graph problem, where you search for all nodes (user IDs) connected to your node (your user) by a distance < 3.

The beauty of that approach is that it would still work with the code above, just that the findFriendsAndTheirFriends() function would do the graph search behind the scenes.

like image 108
Andrew E Avatar answered Sep 19 '25 23:09

Andrew E