JPB
JPB

Reputation: 39

How to chain array of promises / functions with arguments using .reduce() in javascript

I'm looking to loop through an array of functions that return a promise, using reduce(). The goal is to chain them so they wait for the current iteration to be finished before starting the new one.

This is especially confusing to me since I need to pass on the array index and another argument.

I know there's a few examples here and on google of that, but all of them are explained too abstractly for me and I can't understand them the way they are explained.

Here's my code (which is now working after edits):

getAllPostsInfo(maxNumberOfPosts)

.then((posts) => {                                                                      
    printIntroMsg(posts)
    var arrayOfFunctions = posts.map( () => {
        return main
    })

    arrayOfFunctions.reduce((p, fn, i) => {
    return p.then( () => {

        // you may customize what you pass to the next function in the chain
        // and you may accumulate prior results in some other data structure here
        return fn(posts, i);
    });
    }, Promise.resolve() )
        .then(result => {
            // all done here
        }).catch(err => {
            // error here
        });
})

Below is the function that is in each array of arrayOfFunctions. It gets passed to it using posts.map() in the above code. I think using .map() does it correctly.

function main(posts, i) {
    return new Promise( (resolve,reject) => {
        findAndInsertLinksToPosts(posts[i], posts,i)

        .then( () => {
            findAndinsertImgsIntoPostContentandThumbnail(posts[i],posts,i)

            .then( () => {
                console.log("FINISHED!")
                resolve()
            })
        })
    })
}

Upvotes: 1

Views: 3301

Answers (1)

jfriend00
jfriend00

Reputation: 707416

This question is pretty hard to follow and the code appears to contain a number of mistakes so it's hard to figure out from that.

But, if I go back to your brief description, it sounds like may you want to execute an array of functions (that each return a promise) in sequence, waiting for one to finish before starting the next and you seem to want to use .reduce() to do that. If that's the case, then you can do this:

let arrayOfFunctions = [...];

arrayOfFunctions.reduce((p, fn, index) => {
    return p.then(val => {
        // you may customize what you pass to the next function in the chain
        // and you may accumulate prior results in some other data structure here
        return fn(val);
    });
}, Promise.resolve()).then(result => {
    // all done here
}).catch(err => {
    // error here
});

This code passes the resolved result of the previous promise to the next function in the array. You can obviously adapt that however you want. The resolved result of a .reduce() chain like this is the resolved result of the last promise. If you want to accumulate a result from all the operations, then one would typically either pass an object to each function where the resolved result is added to that object each time or create a side variable (like an array) where you accumulate results.

To answer your further questions:

what is Promise.resolve()

That, by itself, creates a resolved promise. We are using .reduce() to create a chain of promises as in x.then().then().then().then(). To do that, we need a promise to start off the chain. We use Promise.resolve() to initiate the chain. So, it's essentially Promise.resolve().then().then().then() where inside each .then() handler, we execute the next function in the array and return its promise (thus adding it to the chain).

what is val?

val is the resolved value of the previous promise in the chain. It will initially be undefined because the first promise is Promise.resolve() which doesn't have a resolved value. After that, it will be whatever each function that returns a promise resolves to. The .reduce() scheme you asked for, lends itself to passing the first result onto the second function, the second result onto the third function and so on.

Where do I put my arguments (posts, i) needed for the function to work?

return fn(val) is where your functions from the array of functions are called. If they need arguments, that's where you put them. Your question asked about an array of functions and did not describe what arguments you needed for those functions (I could not figure out your code well enough to understand that). If you want further help with that, then please describe in more detail exactly what arguments you need to pass to each function in the array of functions you said you started with.


OK, maybe I finally understand what you're trying to do. Here's my take on what you're trying to do.

  1. You want to call getAllPostsInfo() to get a list of posts.
  2. You then want to call printInfoMsg(posts)
  3. You then want to call findAndInsertLinksToPosts() and then findAndInsertImgsIntoPostsContentandThunbnail() on each one of those posts.
  4. And, you want these operations serialized so you operate on one post and only start on the next when the previous one is done (I'm not sure why you have that restriction)

If that's the case, then this is what I would suggest:

getAllPostsInfo(maxNumberOfPosts).then(posts => {
    // I'm assuming this is not asycnhronous
    printIntroMsg(posts);
    
    // now serially process each post
    return posts.reduce((promise, post, i) => {
        return promise.then(() => {
            return findAndInsertLinksToPosts(post, posts, i).then(() => {
                return findAndInsertImgsIntoPostContentandThumnail(post, posts, i);
            });
        });
    }, Promise.resolve());
}).then(() => {
    // all done here
}).catch(err => {
    // error here
});

Upvotes: 6

Related Questions