AngularJS:$ q等待所有人,即使1被拒绝

时间:2013-09-19 06:58:46

标签: angularjs

我一直试图等待Angular的$ q的几个承诺,但似乎没有选择'等待所有即使被拒绝'。 我已经创建了一个示例(http://jsfiddle.net/Zenuka/pHEf9/21/),我希望在所有承诺被解析/拒绝时执行一个函数,这可能吗? 类似的东西:

$q.whenAllComplete(promises, function() {....})

编辑:在示例中,您看到第二个服务失败,之后立即执行$q.all().then(..., function(){...})中的函数。我想等待第五个承诺完成。

8 个答案:

答案 0 :(得分:29)

好的,我自己已经实现了一个基本版本(我只想等待一系列的承诺)。任何人都可以扩展这个或创建一个更清洁的版本,如果他们想:-) 检查jsfiddle以查看它的实际效果:http://jsfiddle.net/Zenuka/pHEf9/

angular.module('test').config(['$provide', function ($provide) {
    $provide.decorator('$q', ['$delegate', function ($delegate) {
        var $q = $delegate;

        // Extention for q
        $q.allSettled = $q.allSettled || function (promises) {
            var deferred = $q.defer();
            if (angular.isArray(promises)) {
                var states = [];
                var results = [];
                var didAPromiseFail = false;
                if (promises.length === 0) { 
                    deferred.resolve(results);
                    return deferred.promise;
                }

                // First create an array for all promises with their state
                angular.forEach(promises, function (promise, key) {
                    states[key] = false;
                });

                // Helper to check if all states are finished
                var checkStates = function (states, results, deferred, failed) {
                    var allFinished = true;
                    angular.forEach(states, function (state, key) {
                        if (!state) {
                            allFinished = false;
                        }
                    });
                    if (allFinished) {
                        if (failed) {
                            deferred.reject(results);
                        } else {
                            deferred.resolve(results);
                        }
                    }
                }

                // Loop through the promises
                // a second loop to be sure that checkStates is called when all states are set to false first
                angular.forEach(promises, function (promise, key) {
                    $q.when(promise).then(function (result) {
                        states[key] = true;
                        results[key] = result;
                        checkStates(states, results, deferred, didAPromiseFail);
                    }, function (reason) {
                        states[key] = true;
                        results[key] = reason;
                        didAPromiseFail = true;
                        checkStates(states, results, deferred, didAPromiseFail);
                    });
                });
            } else {
                throw 'allSettled can only handle an array of promises (for now)';
            }

            return deferred.promise;
        };

        return $q;
    }]);
}]);

答案 1 :(得分:17)

类似于all()如何返回已解析值的数组/散列,来自Kris Kowal的Q的allSettled() function返回一组看起来像的对象:

{ state: 'fulfilled', value: <resolved value> }

或:

{ state: 'rejected', reason: <rejection error> }

由于这种行为非常方便,我已将该功能移植到Angular.js的$ q:

angular.module('your-module').config(['$provide', function ($provide) {
    $provide.decorator('$q', ['$delegate', function ($delegate) {
        var $q = $delegate;

        $q.allSettled = $q.allSettled || function allSettled(promises) {
            // Implementation of allSettled function from Kris Kowal's Q:
            // https://github.com/kriskowal/q/wiki/API-Reference#promiseallsettled

            var wrapped = angular.isArray(promises) ? [] : {};

            angular.forEach(promises, function(promise, key) {
                if (!wrapped.hasOwnProperty(key)) {
                    wrapped[key] = wrap(promise);
                }
            });

            return $q.all(wrapped);

            function wrap(promise) {
                return $q.when(promise)
                    .then(function (value) {
                        return { state: 'fulfilled', value: value };
                    }, function (reason) {
                        return { state: 'rejected', reason: reason };
                    });
            }
        };

        return $q;
    }]);
}]);

归功于:

答案 2 :(得分:4)

angularJS中的promise API基于https://github.com/kriskowal/q。我查看了Q提供的API,它有一个方法allSettled,但是这个方法还没有通过AngularJS使用的端口公开。这是文档

  

all函数返回值数组的promise。当这个   承诺得到满足,数组包含了履行的值   原始承诺,与承诺的顺序相同。如果其中之一   如果承诺被拒绝,则返回的承诺立即生效   拒绝,而不是等待批次的其余部分。如果你想等   对于所有承诺要么得到满足,要么被拒绝,你可以   使用allSettled。

答案 3 :(得分:2)

我最近解决了同样的问题。这就是问题所在:

  • 我有一系列承诺要处理,promises
  • 我想获得所有结果,解决或拒绝
  • 我希望承诺能够并行运行

这就是我解决问题的方法:

promises = promises.map(
    promise => promise.catch(() => null)
);
$q.all(promises, results => {
    // code to handle results
});

它不是一般修复,但它很简单,易于理解。当然,如果您的任何承诺可以解决为null,那么您可以区分拒绝,但它在许多情况下都有效,您可以随时修改catch功能以处理您正在解决的特定问题

答案 4 :(得分:1)

感谢Zenuka的灵感,你可以在https://gist.github.com/JGarrido/8100714

找到我的版本

在这里,它处于当前状态:

.config( function($provide) {
  $provide.decorator("$q", ["$delegate", function($delegate) {
    var $q = $delegate;

    $q.allComplete = function(promises) {

      if(!angular.isArray(promises)) {
        throw Error("$q.allComplete only accepts an array.");
      }

      var deferred = $q.defer();
      var passed = 0;
      var failed = 0;
      var responses = [];

      angular.forEach(promises, function(promise, index) {
        promise
          .then( function(result) {
            console.info('done', result);
            passed++;
            responses.push(result);
          })
          .catch( function(result) {
            console.error('err', result);
            failed++;
            responses.push(result);
          })
          .finally( function() {
            if((passed + failed) == promises.length) {
              console.log("COMPLETE: " + "passed = " + passed + ", failed = " + failed);

              if(failed > 0) {
                deferred.reject(responses);
              } else {
                deferred.resolve(responses);
              }
            }
          })
        ;
      });

      return deferred.promise;

    };

    return $q;
  }]);
})

答案 5 :(得分:1)

解决此问题的更简单方法。

$provide.decorator('$q', ['$delegate', function ($delegate) {
    var $q = $delegate;

    $q.allSettled = $q.allSettled || function (promises) {
        var toSettle = [];

        if (angular.isArray(promises)) {
            angular.forEach(promises, function (promise, key) {
                var dfd = $q.defer();
                promise.then(dfd.resolve, dfd.resolve);
                toSettle.push(dfd.promise);
            });
        }

        return $q.all(toSettle);
    };

    return $q;
}]);

答案 6 :(得分:0)

一个简单的解决方案是使用catch()来处理任何错误并阻止拒绝传播。您可以通过不从catch()返回值或通过使用错误响应解析然后处理all()中的错误来完成此操作。这样$ q.all()将始终执行。我用一个非常简单的例子更新了小提琴:http://jsfiddle.net/pHEf9/125/

...
function handleError(response) {
    console.log('Handle error');
}

// Create 5 promises
var promises = [];
var names = [];
for (var i = 1; i <= 5; i++) {
    var willSucceed = true;
    if (i == 2) willSucceed = false;
    promises.push(
      createPromise('Promise' + i, i, willSucceed).catch(handleError));
}
...

请注意,如果您不从catch()中返回值,则传递给all()的已解析promise的数组将包含针对这些错误元素的undefined。

答案 7 :(得分:-3)

最后使用

$q.all(tasks).finally(function() {
                            // do stuff
                        });