Reputation: 197
I have a set of promises that I want to be run simultaneously. I'm using $q.all
in order to achieve that. When one promise returns a 404 response any other one are unfulfilled. I want to be able to get an error promise individually on $q.all function. How do I do this?
var idPais = 13, idUF = 20;
var promises = [
PaisAPI.list(),
UnidadeFederativaAPI.list(idPais),
MunicipioAPI.list(idUF)
];
$q.all(promises).then(
function(values) {
var paises = values[0].data;
var ufs = values[1].data;
var municipios = values[2].data;
console.log('paises ', paises.length);
console.log('ufs ', ufs.length);
console.log('municipios ', municipios.length);
$scope.paises = paises;
$scope.unidadesFederativas = ufs;
$scope.municipios = municipios;
},
function(error) {
if (error.status == 500) {
alert('Server error');
}
//when one of the promises get a 404 response any other one are unfulfilled
}
);
Upvotes: 0
Views: 1383
Reputation: 95692
You need to ensure that the errors which shouldn't make $q.all()
fail are all handled by the individual promises. You should be sure to handle all of the error cases that you want to allow the $q.all
to complete normally, but if there are other errors possible which should still abort the $q.all
then those should be passed through using $q.reject
. For example to return a length of -1 on an error 404:
function handle404(error) {
if (error.status == 404) {
return { data: { length: -1 }};
}
return $q.reject(error)
}
var promises = [
PaisAPI.list().catch(handle404),
UnidadeFederativaAPI.list(idPais).catch(handle404),
MunicipioAPI.list(idUF).catch(handle404)
];
then the rest of the code as before. Or to just return an empty array in those cases:
function handle404(error) {
if (error.status == 404) {
return { data: []};
}
return $q.reject(error)
}
but that wouldn't be distinguishable from getting back an empty array with no error.
Upvotes: 1
Reputation: 48968
Re-factor the code to put the values on $scope
before executing $q.all
:
var idPais = 13, idUF = 20;
var promiseObj = {};
promiseObj.paises = PaisAPI.list()
.then(function (response) {
var paises = response.data;
console.log('paises ', paises.length);
$scope.paises = paises;
return paises;
}).catch(angular.identity);
promiseObj.ufs = UnidadeFederativaAPI.list(idPais)
.then(function (response) {
var ufs = response.data;
console.log('ufs ', ufs.length);
$scope.unidadesFederativas = ufs;
return ufs;
}).catch(angular.identity);
promiseObj.municipos = MunicipioAPI.list(idUF)
.then(function (response) {
var municipios = response.data;
console.log('municipios ', municipios.length);
$scope.municipios = municipios;
return municipios;
}).catch(angular.identity);
The .catch(angular.identity);
converts a rejected promise to a fulfilled promise. Each API call executes in parallel.
Use $q.all
to wait for all promises to complete either fulfilled or rejected:
$q.all(promiseObj).then(function (dataObj) {
console.log("All API calls complete");
if (dataObj.paises.status) {
console.log("paises failed with ",dataObj.paises.status);
};
if (dataObj.ufs.status) {
console.log("ufs failed with ",dataObj.ufs.status);
};
if (dataObj.municipos.status) {
console.log("municipos failed with ",dataObj.municipos.status);
};
});
One of the things that sets $q.all
apart from other promise libraries is that AngularJS $q.all accepts an object hash of promises.
For more information, see AngularJS $q Service API Reference - $q.all
Upvotes: 2
Reputation: 4505
I suppose it is default behavior in accordance with Promises/A+ spec on which all this stuff is built upon.
Documention for JS native promises states:
The Promise.all() method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
From angular docs for all()
:
Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
All implementations across different frameworks should follow and actually follow this approach.
As a workaround, If your code returns 404 error after, lets say, an ajax call, try to resolve it too instead of rejecting. I mean treat this error as a positive outcome and resolve it with some special value like false
, {error: 404, message: 'Not Found'}
or 0
- to show difference for the client code later. Resolving instead of rejecting should make all()
wait untill all the promises are done.
Upvotes: 3