Home > front end >  nested loop async call in javascript
nested loop async call in javascript

Time:06-27

I have this array of objects

const items = [
  {
    quantity: '2',
    name: 'john',
    type: 'https://api/event/1',
  },
  {
    quantity: '3',
    name: 'jane',
    type: 'https://api/event/2',
  }
]

for each object in the array, I need to call the API n number of times depending on the object quantity value. Calling the API will basically generate a unique link. I know this is not the best for performance but this is a small app and we'd be making between 3-5 api calls in the worst case scenario, usually 1 tbh. In this case, I have 2 objects in the array, the first has 2 API calls and the second has 3 API calls. Every time I call the API I want to save the result (unique link) in an array so that I can email them to the customer who bought them.

This is what I have tried so far but it hasn't worked:


  const promises = []
  const getLinks = async (arr) => {
    for (var i = 0; i < arr.length; i  ) {
      for (var j = 0; j < arr[i].quantity; j  ) {
        const getLink = (owner) => {
          axios.request({
            method: 'POST',
            url: 'https://api.call'
          })
         .then(function (response) {              
            const promise = response.data.url
            promises.push(promise)
          })
         .catch(error => console.log(error))
        }
      }
    }
    const links = await Promise.all(promises)
    console.log('links:', links)  // === [] this is always empty ===
  }

getLinks(items)

One thing I learned is that await cannot be and will slow down the code drastically inside loops

I can't seem to get it to work

CodePudding user response:

  • getLink is never called in your code.
  • Even, if it is called, when Promise.all(promises) runs, the promises array is still be empt because it is populated in the then callback which runs later
  • You are pushing the url from the response to the array instead of pushing the actual promises returned by axios
const promises = []

for (const o of items) {
  for (let i = 0; i < o.quantity; i  ) {
    const promise = axios.request({ method: post, url: o.type })
    promises.push(promise)
  }
}

const responses = await Promise.all(promises)
const urls = responses.map(r => r.data.url)

Or, you could use flatMap get an array of all the promises returned by axios and use Promise.all on the resulting array

const promises = items.flatMap(o => 
  Array.from({ length: o.quantity }, _ => axios.request({ method: post, url: o.type })
)

const responses = await Promise.all(promises)

CodePudding user response:

You are a bit confused about how Promises work.

 /* THIS THING IS A PROMISE -> */ axios.request({
     method: 'POST',
     url: 'https://api.call'
 })
 .then(function (response) {              
     const promise = response.data.url // <-THIS THING IS NOT A PROMISE
 })

The Promise is the value of axios.request(). Therefore if you want the promise you should do this:

const promise = axios.request();

The value inside the .then() is NOT a Promise!! It is the value returned by the promise:

 const promoise = axios.request({
     method: 'POST',
     url: 'https://api.call'
 })
 .then(function (response) {              
     const result = response.data.url;
     return result;
 })

Therefore you have been pushing the completely wrong thing to your promises array! You have been pushing the URL string returned by your api call instead of the promises.

To get your code working correctly is extremely simple:

const getLinks = async (arr) => {
  let promises = []

  for (var i = 0; i < arr.length; i  ) {
    for (var j = 0; j < arr[i].quantity; j  ) {
      const getLink = (owner) => {
        const promise = axios.request({
          method: 'POST',
          url: 'https://api.call'
        })
       .then(function (response) {              
          return response.data.url
        })
       .catch(error => console.log(error))

        promises.push(promise)
      }
    }
  }
  const links = await Promise.all(promises)
  console.log('links:', links)
}

CodePudding user response:

//Map methods return an array
const apiResponse = items.map(async(apiCall) => {
  // call an api here and store its result in a veriable 
  let returnedResult
  await axios.get(apiCall.type)
    .then(response => {
      //refactor the reponse to how you want to store it in the array
      returnedResult = response
    })
  return returnedResult
})

Promise.all(apiResponse)
  .then(response => {
    console.log(response)
    // sent an email here based on the response you get
  })
  • Related