Dofs
Dofs

Reputation: 19207

How can I have two promise called in a Angular service and return combined data

I am having a service which is calling two other async services and returns some combined data, but how can this be done in AngularJS.

I have a controller in which I would like to call these two methods similar to this:

function ServiceC(serviceA,serviceB) {

    var dataA = serviceA.GetAsyncStuff(); //returns promise
    var dataB = serviceB.GetAsyncStuff(); //returns promise

    return 'Greetings ' + dataA + dataB;
}

Is there a smarter way than to actually nest the Then method calls like this:

    function ServiceC(serviceA,serviceB) {

        var dataA = serviceA.GetAsyncStuff().then(function(respA){

            var dataB = serviceB.GetAsyncStuff().then(function(respB){

                 return 'Greetings ' + respA.data + respB.data;
            });
    });
}

This example is of course a bit simplified.

Upvotes: 1

Views: 2859

Answers (5)

Ilan Frumer
Ilan Frumer

Reputation: 32367

Here is a demo plunker: http://plnkr.co/edit/yCrbwnarVDqwC4GBxhGg?p=preview

You need to use $q.all:

app.factory('myService', function($q, serviceA, serviceB, serviceC){

  // $http returns a response object so we need to extract the data
  var fn = function(res){
    return res.data;
  };

  var promises = [
    serviceA.GetAsyncStuff().then(fn),
    serviceB.GetAsyncStuff().then(fn)
  ];

  return $q.all(promises).then(function(data){
    // you can manipulate consolidated data here
    // data.push(serviceC);

    return data;
  });
});

From $q docs:

$q#all(promises);

Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.

Returns

Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.

Upvotes: 5

Gabe
Gabe

Reputation: 2137

Your best bet is to use $q.all, which makes sure that all of the requested promises have been resolved before attempting to execute a function

function ServiceC(serviceA, serviceB, $q) {

    var dataA = serviceA.GetAsyncStuff(); //returns promise
    var dataB = serviceB.GetAsyncStuff(); //returns promise

    $q.all([dataA, dataB]).then(doSomething);
}

EDIT: If you need to return the data, you're going to have to return a promise. You can either just return $q.all or resolve a new promise once $q.all has been resolved, e.g.

function ServiceC(serviceA, serviceB, $q) {

    var dataA = serviceA.GetAsyncStuff(); //returns promise
    var dataB = serviceB.GetAsyncStuff(); //returns promise

    var deferred = $q.defer();
    $q.all([dataA, dataB]).then(function (promises) {
        deferred.resolve('Greetings ' + promises[0] + promises[1]);
    });

    return deferred.promise;
}

Upvotes: 0

Whisher
Whisher

Reputation: 32716

You can chaining promise:

angular.module('app',[])
            .factory('Service1',function($q,$timeout){
                return {
                    f: function(){
                        var deferred = $q.defer();
                        $timeout(function() {
                           deferred.resolve('Service 1');
                        },1000);
                        return deferred.promise;
                    }
                }
            })
            .factory('Service2',function($q,$timeout,Service1){
                return {
                    f: function(){
                        var deferred = $q.defer();
                        Service1.f().then(function(data){
                            $timeout(function() {
                                deferred.resolve('Service 2 '+ data);
                            },1000);
                        });
                        return deferred.promise;
                    }
                }
            })
            .controller('MainCtrl', function($scope,Service2){
                var promise2 = Service2.f();
                    promise2.then(function(data2){
                        console.log(data2);
                    });

            });

or just use

$q.all([Service1,Service2]).then

http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/

Upvotes: 0

madhured
madhured

Reputation: 443

We can call both services and return promise.

myApp.provider("myService", function($http,$q){

    var serviceOneData = {},
    serviceTwoData = {};

    //call service 1

     return {
       serviceOneData : serviceOneData ,
       serviceTwoData : serviceTwoData ,

        getService1: function(){
var deferred = $q.defer();
     $http({method: 'GET',url:'Service1',
                headers:{
                    'Access-Control-Allow-Origin': '*'}}).
                success(function(data,status,header,config){

                serviceOneData = data;
                deferred.resolve();


                })
                .error(function (data,status,header,config) {
                    deferred.reject();
                });

    //call service 2
    return deferred.promise;

        },

         getService2: function(){
var Seconddeferred = $q.defer();
     $http({method: 'GET',url:'Service2',
                headers:{
                    'Access-Control-Allow-Origin': '*'}}).
                success(function(data,status,header,config){
                    serviceTwoData = data;
                    Seconddeferred.resolve();
                })
                .error(function (data,status,header,config) {
                    Seconddeferred.reject();
                });

    //call service 2
    return Seconddeferred.promise;

         },

         getService : getService1().then(getService2()).
            then(
            return serviceOneData + serviceTwoData
          );

     }

} );

Upvotes: 0

Lalit Sachdeva
Lalit Sachdeva

Reputation: 6619

function ServiceC(serviceA,serviceB) {
        var _totalData = {};
        var dataA = serviceA.GetAsyncStuff().then(function(respA){
            _totalData = respA;
        });

        var dataB = serviceB.GetAsyncStuff().then(function(respB){
            _totalData += respB;
            });
        return{ totalData: _totalData}
}

I say first one is better as it is having two different calls and you can add your data and return it at the end and and in your second example you using service under service that would creating a overhead for saving the value of one service till 2nd service get called and call its data, you can see the network pressure second one is taking much time to get the resources and to return the resources.

Upvotes: 0

Related Questions