miladmaaan
miladmaaan

Reputation: 57

Asynchronous function call inside for loop

I am having a problem trying to make my for loop behave synchronously when I am making an asynchronous call in each iteration of the loop.

Here is my code:

pipeline: function(userContext, options, done){

    var orderData = [];

    options.data.forEach(function(store, index){
        store.orders.forEach(function(order){
            shopify.getPipeline(userContext, {'order':order,'storeId':index}, function(result){
                var snapshotId = "30775bf1bb854c5d84c9c2af37bc8fb0";
                var resourceToQuery = config.structrUrls.getUrl("ConfigurationSnapshot") + '/' + snapshotId ;
                var requestOptions = {
                    method: "GET",
                    timeout: 8000
                };
                requestify.request(resourceToQuery, requestOptions)
                    .then(function(snapshotResult){
                        result.snapshots = snapshotResult.getBody().result;
                        result.snapshots.configurationPayload = JSON.parse(snapshotResult.getBody().result.configurationPayload);
                        orderData.push(result);
                    })
                    .catch(function(err){
                        console.log (err);
                        done(err);
                    });
            });
        });
    });

    done(null, orderData);

}

I understand the problem here, but do not know how to remedy it. Let me explain the function:

options.data contains an array of stores, and each store contains an array of orders. For each order, I am calling shopify.getPipeline() for pipeline data (this is a synchronous operation), and in the callback I make a requestify request (a node module used for making http requests) for snapshot data, which I want to append to the result before pushing it onto my "orderData" array. When this all completes, I am calling "done", a callback function, with my orderData. As you can see, since requestify calls are asynchronous, done is called before any data is added to the orderData array.

I think I need to use some kind of promise in order to guarantee the result before calling done, but I have been unsuccessful in implementing a promise into this function. In the documentation for q, it seems like the function I would want to use is promise.all(), which 'Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected'. I'm failing to see how to translate my forEach loop into an array of promises. I was also looking at the async parallel function and ran into the same problem regarding the array of functions.

Any help is greatly appreciated. Thanks!

Upvotes: 1

Views: 1438

Answers (1)

Chris Hunt
Chris Hunt

Reputation: 4030

To construct an array of Promises for use in Promise.all, you can map across the array of stores, and again across the array of orders, returning a Promise for each order which will be resolved or rejected based on the result of requestify.request. Merging the resulting nested array gives you a single array of promises which can then be passed to Promise.all.

Using your example:

pipeline: function(userContext, options, done){
    var nestedPromises = options.data.map.forEach(function(store, index){
        return store.orders.map(function(order){
            return new Promise(function(resolve, reject){
                shopify.getPipeline(userContext, {'order':order,'storeId':index}, function(result){
                    var snapshotId = "30775bf1bb854c5d84c9c2af37bc8fb0";
                    var resourceToQuery = config.structrUrls.getUrl("ConfigurationSnapshot") + '/' + snapshotId ;
                    var requestOptions = {
                        method: "GET",
                        timeout: 8000
                    };
                    requestify.request(resourceToQuery, requestOptions)
                        .then(function(snapshotResult){
                            result.snapshots = snapshotResult.getBody().result;
                            result.snapshots.configurationPayload = JSON.parse(snapshotResult.getBody().result.configurationPayload);
                            resolve(result);
                        })
                        .catch(function(err){
                            reject(err);
                        });
                });
            });
        });
    });
    // Flatten nested array.
    var promises = Array.prototype.concat.apply([], nestedPromises);
    Promise.all(promises).then(function(orderData){
        done(null, orderData);
    }).catch(function(err){
        done(err);
    });
}

Upvotes: 3

Related Questions