Angular $ q.all is called upon completion of the first promise

I am trying to use $ q.all to wait until all promises are resolved, but are called after the first promise is over!

What am I 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

By analyzing the problem a little deeper, this situation occurs when some answers are errors. For example, when the server returns a header("HTTP/1.0 418 I'm A Teapot: " . $filename); , the client console will look like this:

 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 
+5
source share
2 answers

The angular documentation is not in the details, but I believe that $q.all() behaves in this case in the same way as es2015 Promise.all() :

If any of the transmitted promises is rejected, then all Promise immediately rejects the value of the promise, which is rejected, discarding all other promises, whether they were resolved.

Most likely, what happens here is that at least one of your queries fails. Your log statements do not distinguish whether $q.all() successful or unsuccessful, but if it does not work, all you see is the first error.

For the source of the quote, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all .

Edit:

If you want to get all the answers, even if some do not work, then you should add a catch handler to saveAudit to convert errors to successful answers:

 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 answer to see if it contains an error or valid data.

+2
source

As noted by others, $q.all not stable . If one of the promises is rejected, $q.all rejected with the first error.

To make a composite promise resilient , it is a promise that expects all promises to complete a pass or fail, use .catch for each individual promise to convert a .catch > 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 }); 

Two things to take away from this answer:

  • A $q.all promise is not elastic . He rejects with the first promise rejected.
  • A fulfilled promise can be created from a rejected promise by returning the value of the onRejected function of either the .then method or the .catch method.
+2
source

Source: https://habr.com/ru/post/1240919/


All Articles