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

蓝咒 提交于 2019-11-27 03:48:24

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!