Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 278
Tania Rascia Avatar asked Aug 23 '18 15:08

Tania Rascia


2 Answers

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

like image 188
MinusFour Avatar answered Nov 14 '22 23:11

MinusFour


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.

like image 38
NeoVance Avatar answered Nov 14 '22 22:11

NeoVance