How to perform an action once nested fetch-es are complete?

Viewed 131

The code below fetches an array from an API, then for each element of this array, retrieves further data.

fetch('https://reqres.in/api/users')
  .then(r => r.json()).then(r => {
    r.data.forEach(x => {
      fetch('https://reqres.in/api/users')
        .then(r => r.json()).then(r => {
          r.data.forEach(x => console.log(x.id))
        })
    })
  })

I need to perform some action on the data once it is fully retrieved. How to do that?

The problem is that this is a set of Promises which resolve asynchronously. Promise.all() could be used to gather all Promises and work from there - but their amount is not known upfront. In other words, I could use

a = fetch('https://reqres.in/api/users')
b = fetch('https://reqres.in/api/users')
Promise.all([a, b]).then(x => console.log('all resolved here'))

but what is passed to Promise.all() is not known when the script starts.

3 Answers

...but what is passed to Promise.all() is not known when the script starts.

That's okay, you can use map instead of forEach and then wait on the result:

fetch('https://reqres.in/api/users')
  .then(r => r.json()).then(r =>
    Promise.all(r.data.map(x =>
      fetch('https://reqres.in/api/users') // (presumably there's some parameter here, you're not just repeating the same call...)
        .then(r => r.json())
        .then(r => {
          r.data.forEach(x => console.log(x.id))
        })
    ))
  );

The chain the above returns won't settle until all of the promises created in the map have resolved, or any of them has rejected.

You can make array of promises using Array.map:

const allPromises = r.data.map(x => {
  return fetch('https://reqres.in/api/users/' + x)
    .then(r => r.json())
});

Promise.all(allPromises).then(data => console.log('All data is loaded', data))

Maybe it will be the solution for, but it seems like something is wrong with your approach.

fetch('https://reqres.in/api/users')
  .then(r => r.json()).then(r => {
    return Promise.all(r.data.map(x => {
      return fetch('https://reqres.in/api/users')
        .then(r => r.json()).then(r => {
          r.data.forEach(x => console.log(x.id))
        })
      )
    })
  })

In other words you could use nested Promise.all and return it as a result of execution of inner code of your thens. And one important note, you should use map not forEach for iterative asynchronous calls.

Related