uber
uber

Reputation: 5083

Run some code only after fetch statements inside of loops have finished executing

I am building an object out of two different requests from an API. There are three different ids used for those requests at any given time. e.g. const ids = [1, 2, 3]

The whole code is inside of a useEffect, as I would like it to run every time the ids change. Not sure whether this affects or not the code execution order.

I would like my program to behave in this order:

I appreciate this should deal with async requests, and some types of loops don't seem to be appropriate for this according to other posts. I am not sure whether to use async await or simply chain then() one after another.

Code is similar to this:

useEffect(() => {

  // ... some other code ...

  const obj = {}

  // 1st loop
  ids.forEach(id =>{
     fetch(`api/general_info?id=${id}`)
     //whatever comes as response, merge into 'obj' 
  })

  // 2nd loop (to be run only after first loop has finished merging into obj)
  idsToCompare = [[1, 2], [1, 3], [2, 3]] // 

  idsToCompare.forEach(([id1, id2]) =>{
     fetch(`api/compare/?id1=${id1}&id2=${id2}`)
     //whatever comes as response, merge into 'obj' 
  })

  // 3 run this code ONLY AFTER the above loops have finished executing
  // (and obj merge is complete)!
  setCompleteObjToDisplay(obj) // React's setState
  console.log(obj) // complete obj!

}, [ids]) //every time ids change, this code should run.

Upvotes: 0

Views: 714

Answers (2)

Hugh
Hugh

Reputation: 366

It's a bit tricky to use async code with foreach. You could use for in instead and add async before the outer function

useEffect(() => {
(async () => {
  let obj = {} 

  for(const id in ids) {
    const res = await fetch(...)
    obj = {...obj, res}
  }

  idsToCompare = [[1, 2], [1, 3], [2, 3]]

  for(const [id1, id2] in idsToCompare) {
    const res = await fetch(...)
    obj = {...obj, res}
  }

  setCompleteObjToDisplay(obj) // I don't know if this function is asynchronous. Is so, add an await statement
  console.log(obj)
})(), [dependencies])


Upvotes: 2

Isaac Bruno
Isaac Bruno

Reputation: 253

Hugh's answer is the best solution, but I will add another information:

Another way to do it would be using Promises returns to control the main flow.

(async function() {

    const obj = {}

   await new Promise ((resolve, reject) => {
        // 1st loop
        ids.forEach(async (id) => {
            let data = await fetch(`api/general_info?id=${id}`)
            //whatever comes as response, merge into 'obj' 
            resolve(data);
        })
    })

    // 2nd loop (to be run only after first loop has finished merging into obj)
    idsToCompare = [[1, 2], [1, 3], [2, 3]] // 

    await new Promise ((resolve, reject) => {
        idsToCompare.forEach(([id1, id2]) =>{
            let data = await fetch(`api/compare/?id1=${id1}&id2=${id2}`)
            resolve(data);
            //whatever comes as response, merge into 'obj' 
        })
    })

    // 3 run this code ONLY AFTER the above loops have finished executing
    // (and obj merge is complete)!
    setCompleteObjToDisplay(obj)
    console.log(obj) // complete obj!

})();

This example is not the most elegant way to use Promises, but it serves well to show how they work =)

Upvotes: 1

Related Questions