Making serial XHR calls in AngularJS

In most cases what you need is making just a few calls serially or making multiple calls in parallel. For the first case simple promise chaining works fine and for the second AngularJS provides $q.all().

However, what if you need to make arbitrary number of calls serially. For example, we had a case where we wanted to constrain user to upload files to server one at a time.

Following is how it can be achieved:

utilsModule.factory('qExt', ['$q', function ($q) {  
    'use strict';

    return {
      serial: function(calls) {
        if (calls.length === 0) {
          var deferred = $q.defer();
          deferred.resolve();
          return deferred.promise;
        }

        var callWrapper = function(index, deferred) {
        calls[index]().then(function() {
          var nextIndex = index + 1;
          if (nextIndex < calls.length) {
           callWrapper(nextIndex, deferred);
          } else {
            deferred.resolve();
          }
        });

        return deferred.promise;
      };

      return callWrapper(0, $q.defer());
    }
  };
}]);

With this method it is possible to separate the code that controls the way how calls are made from use case code.

Here is how it can be used (this sample takes advantage of Underscore to make it easier to transform original XHR calls but of course same result can be achieved without it):

qExt.serial(_(dataForAllCalls).map(function(dataForOneCall) {  
  return function() {
    return xhrCall(dataForOneCall);
  };
})).then(function() {
  //all calls done
});

Note that this code doesn’t contain error handling but this should be quite easy to add. See full source code and Jasmine tests from this Plunker.