Is it possible to use $q to fire ajax requests synchronously in AngularJS?
I have a long list of vehicles, each vehicle has events associated with them and I need to retrieve the eventdetails of each event when the user expands the listing.
Right now, if the user expands the listing, I am firing up to 15 calls asynchronously and it seems to be causing issues with the API I'm consuming, so I'd like to see if performance is improved if I wait for each request finishes before firing the next.
I'm attempting to implement $q to delay the next request until the previous is finished, however I can't seem to wrap my head around using the service, here is what I currently have:
// On click on the event detail expander
$scope.grabEventDetails = function(dataReady, index) {
    if (dataReady == false) {
        retrieveEventDetails($scope.vehicles[index].events);
    }
}
var retrieveEventDetails = function(events) {
    // events is array
    var deferred = $q.defer();
    var promise = deferred.promise;
    var retrieveData = function(data) {
        return $http({
            url: '/api/eventdetails',
            method: 'POST',
            data: {
                event_number: data.number
            },
            isArray: true
        });
    }
    _.each(events, function(single_event) {
        promise.then(retrieveData(single_event).success(function(data) {
            console.log(data);
        }));
    });
}
This is still firing asynchronously, Where am I going wrong with this?
I understand firing the requests synchronously isn't the best idea, at the moment I just want to see if performance is improved with the API at all.
You don't need $q to implement a promise as $http returns one.
_.each fires all the callbacks without especially waiting the promise.
All you do is call retrieveData for all events whenever your promise is resolved, and since you don't do a first call, it shouldn't even be working
You could do some recursive call like this :
var retrieveEventDetails = function(events) {
    var evt = events.shift();
    $http({
        url: '/api/eventdetails',
        method: 'POST',
        data: {
            event_number: evt.number
        },
        isArray: true
    }).then(function(response){
        console.log(response.data);
        retrieveEventDetails(events);
    });
}
                        I do think you should use $q as some other part of your application might need to get a promise.
A good example would be $routeProvider resolve option.
I made a little demo in plunker.
retrieveData function should return a function (which returns a promise) instead of a just a promise.var retrieveEventDetails = function(events) {
    // events is array
    var deferred = $q.defer();
    var promise = deferred.promise;
    var retrieveData = function(data) {
        return function(){      
            return $http({
                url: '/api/eventdetails',
                method: 'POST',
                data: {
                    event_number: data.number
                },
                isArray: true
            })
        }
    }
    deferred.resolve();
    return events.reduce(function(promise, single_event){
        return promise.then(retrieveData(single_event));
    }, promise);
}
                        I'm not sure you even need $q here. In this example, each piece of data is registered in the controller as soon as it comes back from the call.
Live demo (click).
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
  $scope.datas = myService.get();
});
app.factory('myService', function($http) {
  var myService = {
    get: function() {
      var datas = {};
      var i=0;
      var length = 4;
      makeCall(i, length, datas);
      return datas;
    }
  }
  function makeCall(i, length, datas) {
    if (i < length) {
      $http.get('test.text').then(function(resp) {
        datas[i] = resp.data+i;
        ++i;
        makeCall(i, length, datas);
      }); 
    }
  }
  return myService;
});
Here's a way using $q.all() that you can wait for all of the data to come through before passing it to the controller: Live demo (click).
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
  myService.get().then(function(datas) {
    $scope.datas = datas;
  })
});
app.factory('myService', function($q, $http) {
  var myService = {
    get: function() {
      var deferred = $q.defer();
      var defs = [];
      var promises = [];
      var i=0;
      var length = 4;
      for(var j=0; j<length; ++j) {
        defs[j] = $q.defer();
        promises[j] = defs[j].promise;
      }
      makeCall(i, length, defs);
      $q.all(promises).then(function(datas) {
        deferred.resolve(datas);
      });
      return deferred.promise;
    }
  }
  function makeCall(i, length, defs) {
    if (i < length) {
      $http.get('test.text').then(function(resp) {
        defs[i].resolve(resp.data+i);
        ++i;
        makeCall(i, length, defs);
      })
    }
  } 
  return myService;
});
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With