Tania Rascia
Tania Rascia

Reputation: 1775

How to chain async/await using for data that depends on the first call

Let's say I have an async/await that calls an API that fetches all users.

async function getUsers() {
  const users = await Api.getAllUsers()

  return users.map(user => {
    return {
      id: user.id,
      group: 'data depends on subsequent API call',
    }
  })
}

Within the return map, I have to do another API call to get some data that should be in the same scope.

const groupByUser = Api.getGroupByUserId()

How do I accomplish this? Can I put an async/await within the existing one? Do I create an array of all users ids and somehow map through that? I'm kind of lost on where to go next and any input would be appreciated.

// getUsers() passed into componentDidMount() wrapper

Upvotes: 7

Views: 4571

Answers (2)

NeoVance
NeoVance

Reputation: 404

As long as you are using async/await you can continue to await in your getUsers function. You can reduce the list of users to an async function which will build a self executing async function to await. This function will build the new array.

// MOCK Api
const Api = {
    getAllUsers: async () => [{name: 'user1', id: 1}, {name: 'user2', id: 2}],
    getGroupByUserId: async (id) =>
        new Promise(resolve => setTimeout(() => resolve('users'), id*1000)),
};
// End MOCK Api

async function getUsers() {
  const users = await Api.getAllUsers()

  return await users.reduce((list, user) => {
    return async () => {
        $nextList = await list()
        $nextList.push({
            id: user.id,
            group: await Api.getGroupByUserId(user.id)
        })
        return $nextList
     }
  }, async () => [])()
}

(async () => {
    const allUserGroups = await getUsers();
    console.log(allUserGroups);
})();

// 3s -> [ { id: 1, group: 'users' }, { id: 2, group: 'users' } ]

Edit: On a side note this method makes sure that API requests happen in series, so it is a little easier on the API as far as load, but will be a slower. If you want all of the API requests to happen as soon as possible, the method using Promise.all and map will be a faster since it will make all of the API calls in parallel. ALL, so don't use that method for large lists.

Upvotes: 2

MinusFour
MinusFour

Reputation: 14423

Use an async function for your map function and then use Promise.all on that result.

async function getUsers() {
  const users = await Api.getAllUsers()

  return Promise.all(users.map(async (user) => {
    return {
      id: user.id,
      group: await Api.getGroupByUserId(user.id),
    }
  }))
}

When you map with an async function you essentially get an array of promises. That's why you need Promise.all

Upvotes: 11

Related Questions