Search code examples
javascriptangularjspromiseangular-controller

In calling URLs iteratively using http.get() and resolving using $q.all()


I am implementing this scenario where I have to fetch data from multiple URLs iteratively and process it with some business logic and display on screen. I am implementing this in the controller as it is a requirement. All is well until part-1 and I am getting the 6 promise objects in the promises array. But, I am not getting the data into metricData. I am seeing a null in the console while running in the browser. I am sure that the data is coming in the URL response. I feel I am doing something silly in the $q.all method. Is this correct?

var calculateMutationsInDepth = function(){
        //Part-1
        var promises=[];
        var metricData=[];
        for(var depth=0 ; depth<6 ; depth++){
                var resourceUrl = urlService(depth);
                promises.push($http.get(resourceUrl)
                                  .then(function(response){
                                    return response.data;
                                    },function(status){
                                        return status;
                                }));
                           }
        //Part-2 Resolving the promise array below              
         $q.all(promises).then(function(data){
                        for(var eachResult=0; eachResult < data.length; eachResult++){
                            if(null != data[eachResult]){
                                var eachDataObject = data[eachResult];
        //For debugging         console.log(eachDataObject);
                                    for(var objCount=0; objCount < eachDataObject.length; objCount++){
                                        if(eachDataObject[objCount].scope === "PRJ" || eachDataObject[objCount].scope === "FIL")
                                            metricData.push(eachDataObject[objCount]);
                                    }
                            }
                        }

     });
        if(metricData != null){
                analyzeMutationData(metricData); //Calling a function with the aggregated data array where business logic is present
            }
};

calculateMutationsInDepth();     //Calling the above function

Solution

  • Yes, something silly.

    As written, analyzeMutationData(metricData) is called synchronously whereas metricData is populated asynchronously inside the $q.all(promises).then() callback.

    Also, as written the error handler function(status){ return status; } is inappropriate. Either :

    • omit the error handler entirely and allow any single $http error to prevent further processing in Part 2, or
    • return null, allowing processing in Part 2, and the if(dataObject != null) test in part 2 to filter out any such error.

    Here's the revised code with a few other tidies and a demonstration of what can be done if calculateMutationsInDepth() returns a promise.

    var calculateMutationsInDepth = function() {
        //Part-1
        var depth, promises = [];
        for(depth=0; depth<6; depth++) {
            promises.push($http.get(urlService(depth))
            .then(function(response) {
                return response.data;
            }, function(error) {
                return null; // error recovery - `dataObject` below will be null
            }));
        }
        //Part-2 Aggregate the promises, extract metric data and apply business logic
        return $q.all(promises).then(function(data) { // note `return` here
            var dataObject, i, j, metricData = [];
            for(i=0; i<data.length; i++) {
                dataObject = data[i];
                if(dataObject != null) {
                    for(j=0; j<dataObject.length; j++) {
                        if(dataObject[j].scope === "PRJ" || dataObject[j].scope === "FIL") {
                            metricData.push(dataObject[j]);
                        }
                    }
                }
            }
            // Analyse here, inside the .then()
            if(metricData.length > 0) { // metricData is an array and will never be null, therefore test metricData.length.
                return analyzeMutationData(metricData);
            }
            return null;
        });
    };
    
    calculateMutationsInDepth().then(function(analysis) {
        // all complete
        // `analysis` is either null or whatever `analyzeMutationData(metricData)` returned.
    }).catch(function(error) {
        console.log(error);
    });