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


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?

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.


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
            }
        }
    }
);

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;
        }
    });
});

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/


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

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


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.


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.