javascriptangularjsangular-promise

Serial Execution of many task with $q


Nowadays I am playing with $q of super heroic AngularJS. I have three asynchronous task. Let's call them taskA, taskB and taskC. I want to execute them one by one. Currently I am using $q's all() method. I am doing following:

var taskA = function() {
    var d = $q.defer();
    $timeout(function() {
      $log.log('Task A');
      d.resolve('A is done');
    }, Math.random() * 5000);
    return d.promise;
  };


  var taskB = function() {
    var d = $q.defer();
    $timeout(function() {
      $log.log('Task B');
      // d.resolve('B is done');
      d.reject('B is rejected');
    }, Math.random() * 5000);
    return d.promise;
  };


  var taskC = function() {
    var d = $q.defer();
    $timeout(function() {
      $log.log('Task C');
      //d.resolve('C is done');
      d.reject('C is rejected');
    }, Math.random() * 5000);
    return d.promise;
  };

  $scope.startWithAll = function() {
    $log.log('With all started');
    var promises = [taskA(), taskB(), taskC()];
    $q.all(promises).then(
      //success callback...
      function(result) {
        $log.log(result);
      },
      //error callback
      function(error) {
        $log.log(error);
      },
      //progress callback
      function() {
        //todo implement it
      }
    ).finally(function() {
      $log.log('I am from finally');
    });
  };

The problem with this approach is that all my tasks are executed in parallel. And in success of then I get the result of each task in an array. If any of the task is rejected then rest of the tasks continue the execution but the error callback is executed and I get the error object in the callback. What I want to do is that at first only taskA should be executed then taskB and then taskC if any of them is rejected or failed then rest of the task should not be executed. And there execution should not be parallel it should be serial. To to execute them serially I am doing this :

$scope.startSerial = function() {
    $log.log('Serially started');
    $q.when().then(function(result) {
      $log.log(result);
      return taskA();
    }).then(function(result) {
      $log.log(result);
      return taskB();
    }).then(function(result) {
      $log.log(result);
      return taskC();
    }).finally(function() {
      $log.log('I am from finally');
    });
  };

So this solves my problem. But the thing is that in first callback I am getting undefined, I don't have any problem with that so we can omit this part. But currently I have only three tasks so this approach is fine but what if tomorrow I have more than three task then this will not be a good way to implement. Is there any other way to do so cause I can not keep on adding multiple then just below another. I have created the plunkr for this . What I am thinking is about creating a service which will create this task execution hierarchy but I don't know how to do this.


Solution

  • You can create a loop, that connects all your promises:

      function each(arr, func) {
        for (var i = 0; i < arr.length; i++) {
          func(arr[i], i);
        }
      }
    
      $scope.startSerial = function() {
          var funcs = [taskA, taskB, taskC];
          $log.log('Serially started');
          var s = $q.when();
          each(funcs, function (func) {
              s = s.then(function (result) {
                $log.log(result);
                return func();
              });
          });
          s.finally(function () {
            $log.log('I am from finally');
          });
      }
    

    This should result in the same behavior as your original script, but you can simply adjust the array funcs instead of adding additional blocks of code for each promise.