What happens with $q.all() when some calls work and others fail?

JavascriptAngularjsAngular Promise

Javascript Problem Overview


What happens with $q.all() when some calls work and others fail?

I have the following code:

    var entityIdColumn = $scope.entityType.toLowerCase() + 'Id';
    var requests = $scope.grid.data
      .filter(function (rowData, i) {
          return !angular.equals(rowData, $scope.grid.backup[i]);
      })
      .map(function (rowData, i) {
          var entityId = rowData[entityIdColumn];
          return $http.put('/api/' + $scope.entityType + '/' + entityId, rowData);
      });
    $q.all(requests).then(function (allResponses) {
        //if all the requests succeeded, this will be called, and $q.all will get an
        //array of all their responses.
        console.log(allResponses[0].data);
    }, function (error) {
        //This will be called if $q.all finds any of the requests erroring.
        var abc = error;
        var def = 99;
    });

When all of the $http calls work then the allResponses array is filled with data.

When one fails the it's my understanding that the second function will be called and the error variable given details.

However can someone help explain to me what happens if some of the responses work and others fail?

Javascript Solutions


Solution 1 - Javascript

I believe since the promise library is based on Q implementation, as soon as the first promise gets rejected, the reject callback is called with the error. It does not wait for other promises to resolved. See documentation of Q https://github.com/kriskowal/q. For Q.all this is what is mentioned

> The all function returns a promise for an array of values. When this > promise is fulfilled, the array contains the fulfillment values of the > original promises, in the same order as those promises. If one of the > given promises is rejected, the returned promise is immediately > rejected, not waiting for the rest of the batch.

Solution 2 - Javascript

It's been a while since this question was posted, but maybe my answer might still help someone. I solved a similar problem on my end by simply resolving all promises, but with a return I could process later and see if there were any errors. Here's my example used to preload some image assets:

var loadImg = function(imageSrc) {
    var deferred = $q.defer();

    var img = new Image();
    img.onload = function() {
        deferred.resolve({
            success: true,
            imgUrl: imageSrc
        });
    };
    img.onerror = img.onabort = function() {
        deferred.resolve({
            success: false,
            imgUrl: imageSrc
        });
    };
    img.src = imageSrc;

    return deferred.promise;
}

Later I can see which ones are errorious:

var promiseList = [];
for (var i = 0; i < myImageList.length; i++) {
    promiseList[i] = loadImg(myImageList[i]);
}
$q.all(promiseList).then(
    function(results) {
        for (var i = 0; i < results.length; i++) {
            if (!results[i].success) {
                // these are errors
            }
        }
    }
);

Solution 3 - Javascript

Edit: Only supported in Kris Kowal's Q - but still a useful tidbit

If you want to process all of them without rejecting right away on failure use allSettled

Here's what the docs say:

> If you want to wait for all of the promises to either be fulfilled or > rejected, you can use allSettled.

Q.allSettled(promises)
.then(function (results) {
    results.forEach(function (result) {
        if (result.state === "fulfilled") {
            var value = result.value;
        } else {
            var reason = result.reason;
        }
    });
});

Solution 4 - Javascript

Here is a small answer to it. In this fiddle you can see how it works, if an error occurs in some promise.

$q.all([test1(), test2()]).then(function() {
  // success
}, function() {
  // error
});

http://jsfiddle.net/wd9w0ja4/

Solution 5 - Javascript

I've found a new angular package which add the allSettled functionality to $q in angular:

https://github.com/ohjames/angular-promise-extras

Solution 6 - Javascript

In my case I needed to know when last promise has been resolved no matter if successful or fail. $q.all was not an option because if one fails it goes down immediately. I needed this to make sure user will be redirected no matter what but only if all data are processed (or not) so they can be loaded on next page. So I ended up with this:

  1. Each promise/call implemented also fail callback where "redirect" function is called in both success and fail callbacks.
  2. In this function counter is set, which is increased with each call. If this reaches the number of promises/calls, redirect to next view is made.

I know it's quite a lame way to do it but it worked for me.

Solution 7 - Javascript

Could you not simply handle the error condition on your $http promises before passing them to $q? Promises are chained, so this should work:

return $http.put('/api/' + $scope.entityType + '/' + entityId, rowData).then(function(r){return r;}, angular.noop);

Obviously you could change the noop into any transformation you want but this prevents the rejection which prevents $q.all from failing.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlan2View Question on Stackoverflow
Solution 1 - JavascriptChandermaniView Answer on Stackoverflow
Solution 2 - JavascriptVexterView Answer on Stackoverflow
Solution 3 - JavascriptMarufView Answer on Stackoverflow
Solution 4 - Javascriptuser3232739View Answer on Stackoverflow
Solution 5 - Javascriptbirdy1980View Answer on Stackoverflow
Solution 6 - JavascriptDiomosView Answer on Stackoverflow
Solution 7 - JavascripttoxaqView Answer on Stackoverflow