Uluk Biy
Uluk Biy

Reputation: 49185

Chaining nested promises in the loop

There is a controller that gets say countries and their cities using the service which uses $http.get() to fetch data from the server. Both the countries and the cities are json arrays.

Current implementation

service

service.getCountries = function() {
    return $http.get('url');
}

service.getCities = function(country) {
    return $http.get('url' + country);
}

controller

        var getCountries = function () {
            service.getCountries()
                .success(function (countries) {
                    angular.forEach(countries, function (country) {
                        // process country
                        getCities(country);
                    });
                })
                .error(function () {
                    alert('failed to load countries');
                });
        };

        var getCities = function (country) {
            service.getCities(country)
                .success(function (cities) {
                    angular.forEach(cities, function (city) {
                        // process city
                    });
                })
                .error(function () {
                    alert('failed to load cities of ' + country);
                });
        };

        var doAfterCountriesAndCitiesLoaded = function () {
            // do
        }

        getCountries();
        doAfterCountriesAndCitiesLoaded();

I want to chain the fetching of countries and cities so that doAfterCountriesAndCitiesLoaded() is processed as a last chain.
How this code can be converted to promise chaining with proper error handling?

Upvotes: 1

Views: 571

Answers (2)

Christian
Christian

Reputation: 7852

Return the result of the service.getCountries call (which is a promise) and chain the result with then and catch. In the former's callback function call doAfterCountriesAndCitiesLoaded and in the latter's handle any errors.

var getCountries = function () {
   return service.getCountries()
      .then(function (countries) {
        ....
};

getCountries()
   .then(function(countries){
       doAfterCountriesAndCitiesLoaded(countries);
   }
   .catch(error){
       //handle errors
   }

Upvotes: 1

JLRishe
JLRishe

Reputation: 101662

When you need to execute a number of promises in parallel, you need $q.all, and in order to chain actions, you need to return your promises:

var getCountries = function () {
     return service.getCountries()
         .then(function (result) {
             return $q.all(result.data.map(getCities));
         }).catch(function (result) {
             var message = 'failed to load countries';
             console.log(message);
             throw new Error(message);
         });
};

var getCities = function (country) {
     return service.getCities(country)
         .then(function (result) {
             angular.forEach(result.data, function (city) {
                 // process city
             });
         }).catch(function () {
             var message = 'failed to load cities of ' + country;
             console.log(message);
             throw new Error(message);
         });    
};

var doAfterCountriesAndCitiesLoaded = function () {
      // do
};

getCountries().then(doAfterCountriesAndCitiesLoaded);

Upvotes: 1

Related Questions