Reputation: 14224
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
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
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