Reputation: 19453
I am trying to do multiple $http
call and my code looks something like this:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
How can I have the promise to know all $http
call is successfull? If anyone of it fail, will perform some action.
Upvotes: 12
Views: 11312
Reputation: 8376
You could also use $q.all()
method.
So, from your code:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
You could do:
var promises = [];
data.forEach(function(d) {
promises.push($http.get('/example.com/' + d))
});
$q.all(promises).then(function(results){
results.forEach(function(data,status,headers,config){
console.log(data,status,headers,config);
})
}),
This above basically means execute whole requests and set the behaviour when all have got completed.
Using status you could get to know if any have gone wrong. Also you could set up a different config for each request if needed (maybe timeouts, for example).
From docs which are also based on A+ specs:
$q.all(successCallback, errorCallback, notifyCallback);
Upvotes: 14
Reputation: 10538
Accepted answer is okay, but is still a bit ugly. You have an array of things you want to send.. instead of using a for
loop, why not use Array.prototype.map
?
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
This becomes
var data = ['data1', 'data2', 'data3', ...., 'data10']
var promises = data.map(function(datum) {
return $http.get('http://example.com/' + datum)
})
var taskCompletion = $q.all(promises)
// Usually, you would want to return taskCompletion at this point,
// but for sake of example
taskCompletion.then(function(responses) {
responses.forEach(function(response) {
console.log(response)
})
})
This uses a higher order function so you don't have to use a for
loop, looks a lot easier on the eyes as well. Otherwise, it behaves the same as the other examples posted, so this is a purely aesthetical change.
One word of warning on success
vs error
- success
and error
are more like callbacks and are warnings that you don't know how a promise works / aren't using it correctly. Promises then
and catch
will chain and return a new promise encapsulating the chain thus far, which is very beneficial. In addition, using success
and error
(anywhere else other than the call site of $http
) is a smell, because it means you're relying explicitly on a Angular HTTP promise rather than any A+ compliant promise.
In other words, try not to use success
/error
- there is rarely a reason for them and they almost always indicate a code smell because they introduce side effects.
With regards to your comment:
I have did my own very simple experiment on $q.all. But it only trigger when all request is success. If one if it fail, nothing happen.
This is because the contract of all
is that it either resolves if every promise was a success, or rejects if at least one was a failure.
Unfortunately, Angular's built in $q
service only has all
; if you want to have rejected promises not cause the resultant promise to reject, then you will need to use allSettled
, which is present in most major promise libraries (like Bluebird and the original Q
by kriskowal). The other alternative is to roll your own (but I would suggest Bluebird).
Upvotes: 5
Reputation: 4783
If you are looking to break out on the first error then you need to make your for loop synchronous like here: Angular synchronous http loop to update progress bar
var data = ["data1", "data2", "data3", "data10"];
$scope.doneLoading = false;
var promise = $q.all(null);
angular.forEach(data, function(url){
promise = promise.then(function(){
return $http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data = response.data;
})
.catch(function (response) {
$scope.error = response.status;
});
});
});
promise.then(function(){
//This is run after all of your HTTP requests are done
$scope.doneLoading = true;
});
If you want it to be asynchronous then: How to bundle Angular $http.get() calls?
app.controller("AppCtrl", function ($scope, $http, $q) {
var data = ["data1", "data2", "data3", "data10"];
$q.all([
for(var i = 0;i < data.length;i++) {
$http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data= response.data;
})
.catch(function (response) {
console.error('dataerror', response.status, response.data);
break;
})
.finally(function () {
console.log("finally finished data");
});
}
]).
then(function (results) {
/* your logic here */
});
};
This article is pretty good as well: http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
Upvotes: 7