Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional Chaining of $http-calls with Promises in AngularJS

I have to execute three different $http-calls sequentially which depend on each other. Until now my working solution is something like this:

$http.get(".../1/...").success(function() {
    $http.get(".../2/...").success(function() {
        $http.get(".../3/...").success(function() {
            
        });
    });
});

Now there is a certain change to be made: The first call should be skipped if a condition is true. I could do it like this:

if (skipCallOne) {
    $http.get(".../2/...").success(function() {
        $http.get(".../3/...").success(function() {
            
        });
    });
}
else {
    $http.get(".../1/...").success(function() {
        $http.get(".../2/...").success(function() {
            $http.get(".../3/...").success(function() {
                
            });
        });
    });
}

This obviously leads to massive code replication. I see that this could be reduced, if I used proper functions for the particular $http-calls. But as I understand, a better solution would be to use the $http-promises and properly chain them, like this:

$http.get(".../1/...").then(function() {
    return $http.get(".../2/...");
}).then(function() {
    return $http.get(".../3/...");
}).then(function() {
    
});

But now my question is, how can I conditionally skip the first call with the least code replication?

like image 270
Julian Habekost Avatar asked May 05 '15 15:05

Julian Habekost


3 Answers

You can try this approach:

$q.when(skipCallOne || $http.get(".../1/..."))
  .then(function() {
    return $http.get(".../2/...");
  }).then(function() {
    return $http.get(".../3/...");
  }).then(function() {
});
like image 120
Philipp Andreychev Avatar answered Nov 11 '22 16:11

Philipp Andreychev


You can wrap the $http.get calls in functions that test the condition and return the $http.get promise if the conditions apply or a pre-resolved promise if not.

function callOne() {
    if(condition) {
        return $http.get(".../1/...");
    } else {
        var deferred = $q.defer();
        deferred.resolve();
        return deferred.promise;
    }
}

callOne().then(function() {
    return $http.get(".../2/...");
})
like image 33
Cristian Necula Avatar answered Nov 11 '22 14:11

Cristian Necula


I like to be able to see stuff like this working, so I put this little controller together to demonstrate the promises resolving, with a conditional $http.get running first. I don't claim that this is elegant or as clever as the other answers, though I think it is quite similar to Cristian's solution.

Note: One thing that may make this difficult to understand is answers to this question often show chaining $http calls which are only executed, and for the sake of simplicity, the values those calls are returning are ignored and it may not be immediately clear how to break things apart to actually get at the values.

Demo

app.controller('MainCtrl', function($scope, $q, $http) {
  $scope.x = true;

  var firstHttp = function() {
    var deferred = $q.defer()
    if($scope.x) {
      data = 'x was true'
       deferred.resolve(data);
    } else {
      $http.get('test.json')
      .then(function(data) {
        $scope.data1 = data;
        deferred.resolve(data);
      })
    }  
    return deferred.promise
  }

  $scope.myGetterFn = function() {
    firstHttp()
    .then(function(data) 
    {
      data.data ? $scope.data1 = data : $scope.datax = data;
      $http.get('test2.json')
    .then(function(data2) 
    {
        $scope.data2 = data2
        $http.get('test3.json')
    .then(function(data3) 
    {
          $scope.data3 = data3
    })
    })
    })
  }
});
like image 1
tpie Avatar answered Nov 11 '22 16:11

tpie