Search code examples
promisees6-promise

How can I return an array of Promises from a then clause


I saw a similar question here which doesnt solve my problem. I am trying to run a cron job every 10 hours that lets me get the categories first and then based on the categories, i find the information for each category. How can I simplify the below Promise. I am NOT using Bluebird or Q, this is the native JS promise. Honestly, the code below looks like the same callback hell Promises were supposed to avoid, any suggestions

flipkart.getAllOffers = function () {
    interval(43200, () => {
        flipkart.findAllCategories()
            .then((categories) => {
                flipkart.save('flipkart_categories.json', categories)
                if (categories) {
                    for (let item of categories) {
                        flipkart.findAllForCategory(item.category, item.top)
                            .then((items) => {
                                flipkart.save('flipkart_top_' + item.category + '.json', items)
                            }).catch((error) => {
                                console.log(error)
                            })
                    }
                }
            })
            .catch((error) => {
                console.log(error)
            })
    })
}

function interval(seconds, callback) {
    callback();
    return setInterval(callback, seconds * 1000);
}

Solution

  • If you stop using an extra level of indent just for .then(), then you have a pretty simple structure.

    One .then() handler that contains an if() statement that contains a for loop that contains another async operation

    In this modified version, half your indent comes from your if and for which has nothing to do with promises. The rest seems very logical to me and doesn't at all look like callback hell. It's what is required to implement the logic you show.

    flipkart.getAllOffers = function () {
        interval(43200, () => {
            flipkart.findAllCategories().then((categories) => {
                flipkart.save('flipkart_categories.json', categories)
                if (categories) {
                    for (let item of categories) {
                        flipkart.findAllForCategory(item.category, item.top).then((items) => {
                            flipkart.save('flipkart_top_' + item.category + '.json', items)
                        }).catch((error) => {
                            console.log(error)
                            throw error;     // don't eat error, rethrow it after logging
                        });
                    }
                }
            }).catch((error) => {
                console.log(error)
            })
        })
    }
    

    If flipkart.save() is also async and returns a promise, then you probably want to hook those into the promise chain too.


    You can always create a helper function that may improve the look also like this:

    flipkart.getAllOffers = function () {
        interval(43200, () => {
            flipkart.findAllCategories().then(iterateCategories).catch((error) => {
                console.log(error);
            })
        })
    }
    
    function iterateCategories(categories) {
        flipkart.save('flipkart_categories.json', categories);
        if (categories) {
            for (let item of categories) {
                flipkart.findAllForCategory(item.category, item.top).then((items) => {
                    flipkart.save('flipkart_top_' + item.category + '.json', items);
                }).catch((error) => {
                    console.log(error);
                });
            }
        }    
    }
    

    If you're trying to collect all the results (something your title implies, but your question doesn't actually mention), then you can do this:

    flipkart.getAllOffers = function () {
        interval(43200, () => {
            flipkart.findAllCategories().then(iterateCategories).then((results) => {
                // all results here
            }).catch((error) => {
                console.log(error);
            });
        })
    }
    
    function iterateCategories(categories) {
        flipkart.save('flipkart_categories.json', categories);
        let promises = [];
        if (categories) {
            for (let item of categories) {
                let p = flipkart.findAllForCategory(item.category, item.top).then((items) => {
                    flipkart.save('flipkart_top_' + item.category + '.json', items);
                }).catch((error) => {
                    console.log(error);
                });
                promises.push(p);
            }
        }
        // return promise here that collects all the other promises
        return Promise.all(promises);
    }