0

I have an array where I need to call an API endpoint for each index. Once that is resolved I need to append it in that element. I want to return the updated array once this gets completed for each index of the array.

I tried using async-await in this way

// Let input be [{'x': 1, 'y': 2}, {'x': 11, 'y': 22}, ...]

async function hello(input) {
  await input.forEach(element => {
     fetch(url, options)
     .then((res) => {
        element['z'] = res
     })  
  })
  return input
}

I need to use this function to update my state

hello(data)
.then((res: any) => {

    this.setState((prevState) => ({
        ...prevState,
        inputData: res,
    }))
})

The issue is that I need one more forced render for key 'z' to show. How to resolve this? I don't have much experience using async await so I am not sure if I am using it correctly.

Syscall
  • 19,327
  • 10
  • 37
  • 52
  • Here with `await input.forEach` you expect that the `forEach` method on arrays is returning a promise. Of course, it returns nothing. If you want to `await` a resolved promise, then you have to put it before a function that returns one. – Andreas Dolk May 24 '20 at 14:07
  • Does this answer your question? [Using async/await with a forEach loop](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – Rickard Elimää May 24 '20 at 14:24

2 Answers2

3

The correct way is to use Promise.all and return the promise to be used by the caller function since you want the entire updated input value to be set in state.

In your case forEach doesn't return a promise so await on it is useless.

Also if you use await within the forEach function, you need to be able provide away to let the hello function's .then method call when all promises have resolved. Promise.all does that for you

function hello(input) {
  const promises = [];
  input.forEach(element => {
     promises.push(
          fetch(url, options)
             .then(res => res.json()
             .then((result) => { 
                // return the updated object
                return {...element, z: result};
              })
     ) 
  });
  return Promise.all(promises);
}

...
hello(data)
.then((res: any) => {
    this.setState((prevState) => ({
        ...prevState,
        inputData: res,
    }))
})

P.S. Note that the response from fetch also will need to be called with res.json()

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
1

async/await won't work in loops which uses callback(forEach, map, etc...)

You can achieve your result using for..of loop.

Try this and let me know if it works.

function getResult() {
  return new Promise((resolve) => {
    fetch(url, options)
      .then((res) => {
        return resolve(res);
      })
  })

}

async function hello(input) {

  for (let element of input) {
    let res = await getResult(element);
    element['z'] = res;
  }
}
Akshay Bande
  • 2,491
  • 2
  • 12
  • 29