I'm trying to use $q.all to wait until all promises are resolved but it's called after first promise is finished!
What I'm doing wrong?
function sendAudits(audits) {
var promises = [];
$scope.sendAudits = {
progress: 0
};
angular.forEach(audits, function (audit, idAudit) {
promises.push(saveAudit(audit));
});
$q
.all(promises)
.then(function (data) {
console.log(data);
}, function (errors) {
console.log(errors);
});
}
function saveAudit(audit) {
var filename = audit.header.id + ".txt";
return $http({
method: 'PUT',
url: '/audits/audits.php?filename=' + encodeURIComponent(filename),
data: AuditSvc.getPlainAudit(audit.header.id)
}).finally(function () {
$scope.sendAudits.progress += 1;
console.log("FINALLY: " + audit.header.id);
});
}
EDIT
Analysing a little bit deeper the problem, this situation occurs when some of the responses are error. For example when the server returns header("HTTP/1.0 418 I'm A Teapot: " . $filename);
, client console would be like:
PUT http://localhost:8182/audits/audits.php?filename=1.txt 418 (I'm A Teapot: 1.txt)
FINALLY: 1
Object {data: "", status: 418, config: Object, statusText: "I'm A Teapot: 1.txt"}
PUT http://localhost:8182/audits/audits.php?filename=2.txt 418 (I'm A Teapot: 2.txt)
FINALLY: 2
PUT http://localhost:8182/audits/audits.php?filename=3.txt 418 (I'm A Teapot: 3.txt)
FINALLY: 3
PUT http://localhost:8182/audits/audits.php?filename=4.txt 418 (I'm A Teapot: 4.txt)
FINALLY: 4
$q.all
is not resilient As noted by others, $q.all
is not resilient . If one of the promises is rejected, the $q.all
is rejected with the first error.
To create a resilient composite promise, that is a promise that waits for all the promises to complete pass or fail, use .catch
on each individual promise to convert the rejected promise to a successful promise.
var resilientPromises = [];
angular.forEach(promises, function(p) {
var resilientP = p.catch( function(result) {
//return to convert rejection to success
return result;
});
resilientPromises.push(resilientP);
});
$q.all(resilientPromises).then( function (results) {
//process results
});
The two things to take away from this answer:
$q.all
promise is not resilient . It is rejected with the first rejected promise. .then
method or the .catch
method. For more information, see You're Missing the Point of Promises
The angular documentation doesn't go into detail, but I believe $q.all()
behaves in this case the same way as the es2015 Promise.all()
:
If any of the passed in promises rejects, the all Promise immediately rejects with the value of the promise that rejected, discarding all the other promises whether or not they have resolved.
Most likely what is happening here is that at least one of your requests is failing. Your log statements don't distinguish whether the $q.all()
succeeded or failed but if it is failing all you will see is the first error.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all for the source of the quote.
Edit:
If you want to get all of the responses, even when some fail, then you should add a catch
handler in saveAudit
to convert the failures into successful responses:
function saveAudit(audit) {
var filename = audit.header.id + ".txt";
return $http({
method: 'PUT',
url: '/audits/audits.php?filename=' + encodeURIComponent(filename),
data: AuditSvc.getPlainAudit(audit.header.id)
}).catch(function(error) {
return { error:error};
})
.finally(function () {
$scope.sendAudits.progress += 1;
console.log("FINALLY: " + audit.header.id);
});
}
and then you need to check each response to see whether it contains an error or valid data.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.