0

I have 2 http calls needed to be reslove before initializing my controller, currenly using Angular UI router and I have resolve map under $stateProvider.state('stateName', {stateObject}) my stateObject as bellow

    $stateProvider.state('stateName', {
        url  : '/myURL',
        params: {
                 data1: undefined,
                 data2 : undefined
            },
        resolve: {
            dataTobeResolve : function($stateParams,$q) {

                var deferred = $q.defer();
                var deferredObj = {};
                deferredObj.d1 = $q.defer();
                deferredObj.d2 = $q.defer();

                var result = {
                    data1: {},
                    data2: {}
                }

                if(angular.isDefined($stateParams.data1)) {
                    result.data1 = $stateParams.data1;
                    deferredObj.d1.resolve();
                }
                else {
                    httpCall().then(function(response) {
                        AsyncMethodCall(response.data).then(function(resolvedData) {
                            result.data1 = resolvedData;
                            deferredObj.d1.resolve();       
                        });
                    });
                }

                if(angular.isDefined($stateParams.data2)) {
                    result.data2 = $stateParams.data2;
                    deferredObj.d2.resolve();
                }
                else {
                    AsyncMethodCall().then(function(resolvedData) {
                        result.data2 = resolvedData;
                        deferredObj.d2.resolve();       
                    });
                }

                $q.all(deferredObj).then(function() {
                    deferred.resolve(result);
                });
                return deferred.promise;
            }
        }
    });

how ever despite of deferredObj.d1 being resolve controll goes to then(function(){}) of $q.all(deferredObj) which is unexpected behaviour and I belive that all of the promise maps of deferredObj should be resolve before this line get executed, my controller get initialize despite of one of the promises being resolved

Sohail Faruqui
  • 442
  • 11
  • 27

2 Answers2

1

Since $q.all, $http, and the .then method all returns promises, the is no need to manufacture a promise with $q.defer. Only use $q.defer to make promises from old style callback-only asynchronous APIs.

Use $q.when to make a promise from a synchronous source or from a promise from outside the AngularJS framework.

resolve: {
    dataTobeResolve : function($stateParams,$q) {

        var d1Promise;
        var d2Promise;

        if(angular.isDefined($stateParams.data1)) {
            d1Promise = $q.when($stateParams.data1);
        }
        else {
            d1Promise = httpCall()
              .then(function(response) {
                   return response.data;
            });
        }

        if(angular.isDefined($stateParams.data2)) {
            d2Promise = $q.when($stateParams.data2)
        }
        else {
            d2Promise = httpCall2()
              .then(function(response) {
                  return response.data;      
            });
        }

        return $q.all([d1Promise, d2Promise]);
    }
}

In the above example, two promises are created by either $q.when of a parameter or derived from a promise-based asynchronous API. A composite promise is created from the two promises with the $q.all method.

Using derived promises has the advantage that rejections are automatically carried forward to the final promise. As currently written, the $q.defer method, will hang the state change if either of the XHR calls has an error from the server.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
0

I have found the issue and it is due to passing deferred object into $q.all() rather than hash/array of promises. so following code will solve the problem

$stateProvider.state('stateName', {
    url  : '/myURL',
    params: {
             data1: undefined,
             data2 : undefined
        },
    resolve: {
        dataTobeResolve : function($stateParams,$q) {

            var deferred = $q.defer();
            var promises = {};
            var d1 = $q.defer();
            var d2 = $q.defer();
            promises.d1 = d1.promise;
            promises.d2 = d2.promise;

            var result = {
                data1: {},
                data2: {}
            }

            if(angular.isDefined($stateParams.data1)) {
                result.data1 = $stateParams.data1;
                d1.resolve();
            }
            else {
                httpCall().then(function(response) {
                    AsyncMethodCall(response.data).then(function(resolvedData) {
                        result.data1 = resolvedData;
                        d1.resolve();       
                    });
                });
            }

            if(angular.isDefined($stateParams.data2)) {
                result.data2 = $stateParams.data2;
                d2.resolve();
            }
            else {
                AsyncMethodCall().then(function(resolvedData) {
                    result.data2 = resolvedData;
                    d2.resolve();       
                });
            }

            $q.all(promises).then(function() {
                deferred.resolve(result);
            });
            return deferred.promise;
        }
    }
});

but still I appreciate if someone provide better solution

Sohail Faruqui
  • 442
  • 11
  • 27