0

If I didn't need to worry about asynchronous code, this is what it'll look like:

app.get('/user/:username/events', async (req, res) => {

    // Grab all events by that user
    let events = await axios.get(`${baseApiUrl}/users/${req.params.username}/events`, options).then(d => d.data)

    // Loop through events
    events.forEach(event => {

        // If event type is A_Event
        if (event.type === 'A_Event') {

            // Loop through objArr array
            event.payload.objArr.forEach(async obj => {

                // Grab additional data through endpoint
                // that lives in obj
                let data = await axios.get(obj.url)

                // Append data back onto obj
                obj.objData = data
            })
        }
    })

    // return events obj with objData appended to each obj within
    // objArr
    res.json({ events })

})

But this doesnt work because it returns events before its done grabbing all the data.

I tried doing something with Promise.all but couldnt get what I needed to get.

events.map(async event => {
    if (event.type === 'A_Event') {
        getData(event.payload.objArr)
            .then(objData => {

                // This returns an array of objData that
                // then needs to be added back to the specific obj
                // it came from
                event.payload.objArr.forEach((obj, i) => {
                    obj.objData = objData[i]
                })
            })
    }

    res.json({ events })
})


const getData = async objArr => {
    const requests = objArr.map(async obj => {
        const data = await axios.get(obj.url, options)
        return {
            stats: data.data.stats,
            created_at: data.data.created_at
        }
    })

    return Promise.all(requests)
}

Would really appreciate the help with this. Been stuck on this problem for a good bit.


EDIT

Tried switching to a for of loop according to this post: Using async/await with a forEach loop... No luck but I think its a step in the right direction.

 events.forEach(async event => {

        if (event.type === 'A_Event') {

            for (let obj of event.payload.objArr) {
                let objData = await axios.get(obj.url, options)
                obj.objData = objData
                console.log('setting value')
            }
        }
    })

    res.json({ events })
    console.log('returned value')

I get returned value before I get setting value...

Syn
  • 938
  • 1
  • 10
  • 21
  • What's your output ? And what is the expected result ? – Mickael B. Jan 22 '20 at 19:21
  • Output is the initial response from the first axios call. Which is `events`, but it doesnt contain the key/value `objData` within each item in `objArr` – Syn Jan 22 '20 at 19:32
  • 1
    Does this answer your question? [Using async/await with a forEach loop](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – Klaycon Jan 22 '20 at 19:36
  • @Klaycon Let me test it out. – Syn Jan 22 '20 at 19:37
  • Your `await axios.get(obj.url)` in the inner inner forEach loop doesn't do what you think it does. `forEach` does not wait for each previous async function to resolve before calling the next, it starts them up all at the same time and then immediately proceeds to the next line. **forEach is not asynchronous.** Use a regular loop `for(let obj of event.payload.objArr) {...` instead, see the linked dupe target for more – Klaycon Jan 22 '20 at 19:37
  • @Klaycon I edited the post. I tried a `for of ` loop but it isnt working correctly. I am still returning the response before the `events` arr gets updated – Syn Jan 22 '20 at 19:55
  • @Syn you only changed the inner forEach and not the outer one. Using a forEach guarantees that any further `async/await`age will *not* wait for the loop to complete before proceeding to code after the loop. Change both of them to `for..of` – Klaycon Jan 22 '20 at 20:08

5 Answers5

0
Promise.all(requests);

Will return a promise so you can handle it somewhere else like this:

getData(objaArr).then((result) => {
  // your code
});
enbermudas
  • 1,603
  • 4
  • 20
  • 42
  • Where will I return the res then? Since not every obj will be of type `A_Event`, I cant put the res after the promise because then it wont return everything. – Syn Jan 22 '20 at 19:31
0

In your second block of code, just await for Promise.all()

return await Promise.all(requests);

hayoola
  • 121
  • 1
  • 6
0

You would use Promise.all as suggested in the other answer(s).

To retrieve the result you would do something like:

let [languages, currencies, countries] = await Promise.all([
    this.downloadLanguages(),
    this.downloadCurrencies(),
    this.downloadCountries()
  ])

Your variables can be assigned values returned from the promises using this call structure.

Screll
  • 266
  • 2
  • 12
0

You could use good old-fashioned for loops:

for (let i = 0; i < events.length; i++) {
    if (events[i].type === 'A_Event') {
      for (let j = 0; j < events[i].payload.objArr.length; j++) {
        const data = await axios.get(events[i].payload.objArr[j].url);
        events[i].payload.objArr[j].objData = data;
      }
    }
  }
Soma
  • 1
  • 2
0

Got it working. I needed to switch the initial loop to a for of. Not just the inner loop. Thanks everyone!

for (let event of events) {
    if (event.type === 'A_Event') {
        for ( let obj of event.payload.objArr) {
            let objData = await axios.get(obj.url, options)
            obj.objData = objData.data
        }
    }
}
Syn
  • 938
  • 1
  • 10
  • 21