user70192
user70192

Reputation: 14224

Running $http requests sequentially in AngularJS

I have an array of items on my $scope. For each of those items, I need to run three $http requests. These requests must run in a specific order, no matter whether there was a failure of not. I'm not sure how to do this elegantly, with the promise paradigm. I have a lot of duplicate code and it looks really confusing. I have to be doing this wrong. Currently, I have the following:

$scope.items = getItems();
$scope.currentIndex = 0;

$scope.executeItem = function() {
  $http.get($scope.items[$scope.currentIndex].urlA).then(
    function (resA) {
      $scope.items[$scope.currentIndex].urlAWorks = true;
      $http.get($scope.items[$scope.currentIndex].urlB).then(
        function (resB) {
          $scope.items[$scope.currentIndex].urlBWorks = true;

          $http.get($scope.items[$scope.currentIndex].urlC).then(
            function (resC) {
              $scope.items[$scope.currentIndex].urlCWorks = true;
              $scope.currentIndex = $scope.currentIndex + 1;
              $scope.executeItem();
            },

            function (errC) {
              $scope.items[$scope.currentIndex].urlCWorks = false;
              $scope.currentIndex = $scope.currentIndex + 1;
              $scope.executeItem();
            }
          )
        },
        function (errB) {
          $scope.items[$scope.currentIndex].urlBWorks = false;
        }
      );
    },

    function (errA) {
      $scope.items[$scope.currentIndex].urlAWorks = false;
      $http.get($scope.items[$scope.currentIndex].urlB).then(
        function (resB) {
          $scope.items[$scope.currentIndex].urlBWorks = true;

          $http.get($scope.items[$scope.currentIndex].urlC).then(
            function (resC) {
              $scope.items[$scope.currentIndex].urlCWorks = true;
              $scope.currentIndex = $scope.currentIndex + 1;
              $scope.executeItem();
            },

            function (errC) {
              $scope.items[$scope.currentIndex].urlCWorks = false;
              $scope.currentIndex = $scope.currentIndex + 1;
              $scope.executeItem();
            }
          )
        },
        function (errB) {
          $scope.items[$scope.currentIndex].urlBWorks = false;
        }
      );
    }
  );
};

Am I really chaining promises correctly? This looks WAY off.

Thank you

Upvotes: 3

Views: 2436

Answers (2)

New Dev
New Dev

Reputation: 49610

You are underusing promises :) Since .then returns a promise, you could do:

$http.get(urlA)
  .then(function(dataA){
    DoStuffWithA(dataA);

    return $http.get(urlB);
  })
  .then(function(dataB){
    DoStuffWithB(dataB);

    return $http.get(urlC);
  })
  .then(function(dataC){
    DoStuffWithC(dataC);

    return true;
  })

Upvotes: 3

Ross Rogers
Ross Rogers

Reputation: 24270

Just create references to those functions with a parameter binding. Instead of

      $http.get($scope.items[$scope.currentIndex].urlC).then(
        function (resC) {
          $scope.items[$scope.currentIndex].urlCWorks = true;
          $scope.currentIndex = $scope.currentIndex + 1;
          $scope.executeItem();
        },

        function (errC) {
          $scope.items[$scope.currentIndex].urlCWorks = false;
          $scope.currentIndex = $scope.currentIndex + 1;
          $scope.executeItem();
        }
      )

Do:

var next_thingy = function (worked) {
  return function () {
    $scope.items[$scope.currentIndex].urlCWorks = worked;
    $scope.currentIndex = $scope.currentIndex + 1;
    $scope.executeItem();
   }
}

$http.get($scope.items[$scope.currentIndex].urlC)
  .then(next_thingy(true),next_thingy(false));

Then chain them together:

var req1 = $http.get(...)
var thingies = {}

var thingies.next_thingy = function( worked) {
  return function() {
    var req = $http.get(...)
    ...
    req.then(thingies.next_thingy2(true),thingies.next_thingy2(false))
  }
}
req1.then(thingies.next_thingy(false),thingies.next_thingy(true))
var thingies.next_thingy2 = function(worked2) {
  return function() {
    var req2 = $http.get(...)
    ...
    req2.then(thingies.next_thingy3(true),thingies.next_thingy3(false); 
  }
}
var thingies.next_thingy3 = function(worked3) {
  return function() {
    ...
  }
}

You can fork them all off in parallel and then wait for them to finish with:

var third_reqs = []
$scope.items.forEach(function(item) {
   var third_req_defer = $q.defer()
   third_reqs.push(third_req_defer.promise)
   ...
    var thingies.next_thingy3 = function(worked3) {
      return function() {
       ...
       third_req_defer.resolve()
      }
   }
}) 
$q.all(third_reqs).then(
    function() { $log.log("Finished!")}, 
    function(){ $log.error("some third reqs failed.")})

Upvotes: 1

Related Questions