28

私は Angular の $q でいくつかの約束を待とうとしてきましたが、「約束が拒否された場合でもすべてを待つ」オプションはないようです。私は例を作成しました ( http://jsfiddle.net/Zenuka/pHEf9/21/ )。すべての約束が解決/拒否されたときに関数を実行したいのですが、それは可能ですか? 何かのようなもの:

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

編集: この例では、2 番目のサービスが失敗し、その直後に関数$q.all().then(..., function(){...})が実行されていることがわかります。五つ目の約束が果たされるのを待ちたい。

4

8 に答える 8

31

わかりました、私は自分で基本的なバージョンを実装しました (約束の配列を待ちたいだけです)。必要に応じて、誰でもこれを拡張したり、よりクリーンなバージョンを作成したりできます:-) 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;
    }]);
}]);
于 2013-09-19T09:33:03.727 に答える
20

all()解決された値の配列/ハッシュを返す方法と同様に、Kris Kowal の Q のallSettled()関数は次のようなオブジェクトのコレクションを返します。

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

クレジットは次のとおりです。

于 2014-11-24T21:36:41.750 に答える
4

angularJS の promise API は、 https://github.com/kriskowal/qに基づいています。Q提供するAPI を調べたところ、メソッドallSettledがありましたが、このメソッドは AngularJS が使用するポート経由で公開されていません。これはドキュメントの形式です

all 関数は、値の配列の promise を返します。この約束が履行されると、配列には元の約束の履行値がそれらの約束と同じ順序で含まれます。指定された promise の 1 つが拒否された場合、返された promise は、バッチの残りを待たずにすぐに拒否されます。すべての約束が履行または拒否されるのを待ちたい場合は、allSettled を使用できます。

于 2013-09-19T07:34:29.903 に答える
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;
  }]);
})
于 2013-12-23T17:20:34.907 に答える