Nick
Nick

Reputation: 406

Dealing with multiple asynchronous function calls in a for loop

I am trying to do multiple asynchronous actions: Axios requests inside of a for loop. I want to do something after everything is resolved but there is so much going on I don't know how to do it.

I thought of making my sourcer function async and awaiting it on each iteration (and wrapping the for loop in an async function), but one problem is that sourcer doesn't actually return anything. I don't know how to return from sourcer from inside an Axios "finally" clause. Another problem is that I don't want to await each sourcer call because it would be a hit on performance.

Promise.all sounds like the right direction to take but I don't know how to implement it with this for loop.

Here is the relevant part of my code (ts is a large array of objects):

.then(ts => {
                // Create an association object that determines each media item's source
                const sourcer = media => { // Input is either [image filename, image url] or [image filename, image url, video filename, video url]
                    // Test to see if the original URL works
                    let validURL = true
                    axios.get(media[1])
                        .then(resp => {
                            if (resp.status.toString()[0] !== '2') validURL = false
                        })
                        .catch(resp => {
                            if (resp.status.toString()[0] !== '2') validURL = false
                        })
                        .finally(() => {
                            let newSources = JSON.parse(JSON.stringify(this.state.sources))
                            let newModals = JSON.parse(JSON.stringify(this.state.modals))
                            if (validURL) newSources[media[0]] = media[1]
                            // If the original URL does not work, pull media item from server
                            else newSources[media[0]] = `http://serveripaddress/get_media?filename=${media[0]}`
                            newModals[media[0]] = false
                            this.setState({ sources: newSources, modals: newModals })
                        })
                    if (media.length > 2) { // If the media item is a video, do the same checks
                        let validVURL = true
                        axios.get(media[3])
                            .then(resp => {
                                if (resp.status.toString()[0] !== '2') validVURL = false
                            })
                            .catch(resp => {
                                if (resp.status.toString()[0] !== '2') validVURL = false
                            })
                            .finally(() => {
                                let newSources2 = JSON.parse(JSON.stringify(this.state.sources))
                                let newThumbnails = JSON.parse(JSON.stringify(this.state.thumbnails))
                                if (validVURL) newSources2[media[2]] = media[3]
                                else newSources2[media[2]] = `http://serveripaddress/get_media?filename=${media[2]}`
                                newThumbnails[media[0]] = media[2] // Add an association for the video and its thumbnail
                                this.setState({ sources: newSources2, thumbnails: newThumbnails })
                            })
                    }
                }
                for (let t of ts) {
                    if (t.media) for (let m of t.media) sourcer(m)
                    if (t.preview_media) sourcer(t.preview_media)
                    if (t.video) sourcer(t.video)
                }
            })

I want to do something after ts has been iterated through and all sourcer calls are completed.

I'm not fishing for someone to write my code for me but a nudge in the right direction would be greatly appreciated.

Upvotes: 0

Views: 41

Answers (1)

mwilson
mwilson

Reputation: 12990

axios.get will return a Promise, so simply build up your array of Promises and use Promise.all

So, in your case, instead of executing the http call and waiting on the response, just add it to your array.

Something like this will work. I removed your code that was handling the response of each individual get request. You can merge that code (or just copy/paste) into where I put the placeholder below:

.then(ts => {
    // Create an association object that determines each media item's source
    const sourcer = media => { // Input is either [image filename, image url] or [image filename, image url, video filename, video url]
        // Test to see if the original URL works
        let validURL = true;
        const promises = [];
        promises.push(axios.get(media[1]));
        if (media.length > 2) { // If the media item is a video, do the same checks
            let validVURL = true;
            promises.push(axios.get(media[3]));
        }
    }
    for (let t of ts) {
        if (t.media)
            for (let m of t.media) sourcer(m)
        if (t.preview_media) sourcer(t.preview_media)
        if (t.video) sourcer(t.video)
    }

    // Execute the Promises
    Promise.all(promises).then( results => {
        const media1 = results[0];
        const media3 = results[1];
        // TODO: Run your code for media1/media3 results
    })
})

Upvotes: 1

Related Questions