al3
al3

Reputation: 104

Node.js - Run function with callback iteratively

I am developing an API in javascript using node.js

What I'm trying to do is call a function which fires a chain of promises and on callback it function returns an array with information for one product.

What I want to do is to run this function as many times as required to form a list of products in JSON format.

But this list of products needs to be created before it's sent back to the user, which is where I'm struggling.

This is the function I have:

exports.findProduct = (ingredient, store, callback) => {
            products.getProductData(ingredient, store)
        .then( data => products.getProducts(data))
        .then( productList => products.filterProductData(productList))
        .then( selectedProduct => callback(null,selectedProduct))//get individual products and add to list
        .catch( err => callback(err))
}

I call it like this

products.findProduct(ingredient, 'Waitrose', (err, data) => {

    if (err) {
        //res.send(status.badRequest, {error: err.message})
    } else {
        res.send(data)
        res.end()
    }

})

The res.send(data) sends the data from the callback back to the server, but what if I want to run this function again and update an array and only then send the array to the server?

Any help of tips would be very appreciated.

Upvotes: 0

Views: 79

Answers (1)

Alex Young
Alex Young

Reputation: 4039

A promise-based possible implementation that allows getAllProducts to call itself recursively.

exports.findProduct = (ingredient, store) => {
    return new Promise((resolve, reject) => {
         products.getProductData(ingredient, store)
        .then( data => products.getProducts(data))
        .then( productList => products.filterProductData(productList))
        .then( selectedProduct => resolve(selectedProduct))
        .catch( err => reject(err))
    }
}



function getAllProducts(ingredientList, shopName) {  
    const allProducts = [];
    return new Promise((resolve, reject) => {
        products.findProducts(ingredientList, shopName)
        .then(product => {
            allProducts.push(...product);

            if(/*need more products*/) {
                getAllProducts(ingredientList, 'Tesco')
                .then(newProducts => {
                    allProducts.push(...newProducts);
                    resolve(allProducts);
                })
                .catch(err => reject(err));
            } else {
                resolve(allProducts);
            }
        })
        .catch(err => reject(err));
    }
}

Called like this...

getAllProducts(ingredientList, 'Waitrose')
.then(products => {
    res.send(products);
    res.end();
}).catch(err => {
    res.send(status.badRequest, {error: err.message});
    res.end();
});

Edit:

The above approach is valid if you need to determine whether to make more call based on the result of a previous call. If you know up front that you need to make multiple calls, e.g. if you know you want to check Tesco, Waitrose and Asda, then there is a simpler way using Promise.all

exports.findProduct = (ingredient, store) => {
    return new Promise((resolve, reject) => {
         products.getProductData(ingredient, store)
        .then( data => products.getProducts(data))
        .then( productList => products.filterProductData(productList))
        .then( selectedProduct => resolve(selectedProduct))
        .catch( err => reject(err))
    }
}

function makeMultipleCalls() {  
    const stores = ['Waitrose', 'Tesco', 'Asda'];
    const promises = [];

    for (var store in stores) {
        promises.push(product.findProduct(ingredientList, store);
    }

    Promise.all(promises)
    .then(results => {
        res.send(results.reduce((a,b) => a.concat(b)));
        res.end();
    })
    .catch(err => {
        //deal with error
    });
}

Upvotes: 1

Related Questions