op_exchanger
op_exchanger

Reputation: 94

Resolve a promise nested inside an array of objects

I have an array of objects that I get from database.

const users = await User.findAll();

To each of them, I want to call an async function user.getDependency() to get another related table, and devolve it nested in the object to a final format like this:

[
  User {
    attr1: value,
    attr2: value,
    ...
    Dependency: {
      attr1: value,
      ...
    }
  },
  User {
    ...

and so on

Now, I get my problem. The only possibilities I am being able to think involving async and loops are 2:

Option 1: mapping them and getting the promise back:

  users.forEach((user) => {
    user.dependency= user.getDependency();
  });

With this I get similar to the desired outcome in terms of format, only that instead of the resolved thing i get obviously Dependency: Promise { <pending> }, that is, the promises all nested inside each user object of the array. And I don't know how to procceed with it. How to loop through all the user objects and resolve this promises they have inside?

Option 2: return the promises to be resolved in Promise.all

const dependencies = Promise.all(users.map((user) => {
  return user.getDependency();
}));

This give me a separated array with all the dependecies I want, but that's it, separated. I get one array with the objects users and another with the objects dependencies.

What is bugging me specially is that I am thinking that there must be a simple straightforward way to do it and I am missing. Anyone have an idea? Much thanks

Upvotes: 0

Views: 1818

Answers (3)

Mayur
Mayur

Reputation: 160

you're right there is a straightforward way to resolve the promise as the users variable is not a promise waiting to be resolved, you should try

users.then((userList) => {
       // function logic here
})

the userList in the then function should be iterable and contain the desired user list

also you can also handle the rejected part of the promise, if at all the query fails and the promise returns an error:

users.then((error) => {
   // error handling
})

you should give this document a glance to get more info about handling promises to further know what goes on in that query when it returns a promise

Upvotes: 0

YuraGon
YuraGon

Reputation: 147

I guess the best way is to do like this. Mapping users, updating every object (better to do this without mutation), and using Promise.all

const pupulatedUsers = await Promise.all(users.map(async (user) => ({
  ...user,
  dependency: await getDependency(user.dependency_id)
  // or something like getDependency
  // function that fires async operation to get needed data
}));

Upvotes: 2

richytong
richytong

Reputation: 2452

To build some intuition, here is your forEach example converted with a push in the right direction.

users.forEach((user) => {
  user.getDependency().then(dependency => {
    user.dependency = dependency
  })
})

The above reads "for each user, start off the dependency fetching, then add the dependency to the user once resolved." I don't imagine the above is very useful, however, as you would probably like to be able to do something with the users once all the dependencies have finished fetching. In such a case like that, you could use Promise.all along side .map

const usersWithResolvedDependencies = await Promise.all(
  users.map(user => user.getDependency().then(dependency => {
    user.dependency = dependency
    return user
  }))
)
// do stuff with users

I should mention I wrote a library that simplifies async, and could certainly work to simplify this issue as well. The following is equivalent to the above. map and assign are from my library, rubico

const usersWithResolvedDependencies = map(assign({
  dependency: user => user.getDependency()
}))(users)

Upvotes: 2

Related Questions