0

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.

3
  • 1
    Promise.all: developer.mozilla.org/en-US/docs/web/javascript/reference/… Commented Mar 4, 2020 at 22:45
  • I know how Promise.all works but the functions passed in my case are not constants, they are only called if the conditions in the for loop are met. I don't know how to implement that. Commented Mar 4, 2020 at 22:49
  • use async/await to help reduce the cognitive load of the code Commented Mar 4, 2020 at 22:51

1 Answer 1

1

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
    })
})
Sign up to request clarification or add additional context in comments.

6 Comments

This makes sense, thank you! I assume you meant to declare promises outside the sourcer function. So I can run my catch and finally clauses on the results of Promise.all, and add another then clause to the Promise.all to run code after everything is resolved?
Yea, my bad. You would need that to be at whatever scope that allows you work with it where you need it. Please remember to upvote/mark as answer
I can see that I will be able to execute my code on the promises but how do I access the original values of media in each promise? E.g. if the promise was axios.get(media[0]) but I needed the value of media[1] to complete my code.
It's not 100% clear what your object looks like, but it looks like the media objects is really just t.preview_media or t.video (from your for loop). What I would do is refactor that so you can have a function to search and return the media var you're after. ` Another option would be to create an object of something like { promise: <Promise>, media: <media value> } and push that your promises array. Then, you can have a function actually run Promise.all but instead, you would map your objects so you would have that Promise:Media Value mapping.
Can you update your question with what you settled on. Hard to tell without knowing what it looks like.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.