AngularJS: $q wait for all even when 1 rejected

后端 未结 8 1578
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-01 16:23

I\'ve been trying to wait for a couple of promises with Angular\'s $q but there seems to be no option to \'wait for all even when a promis is rejected\'. I\'ve created an exampl

相关标签:
8条回答
  • 2021-02-01 16:55

    Thanks for the inspiration Zenuka, you can find my version at https://gist.github.com/JGarrido/8100714

    Here it is, in it's current state:

    .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;
      }]);
    })
    
    0 讨论(0)
  • 2021-02-01 16:57

    The promise API in angularJS is based on https://github.com/kriskowal/q. I looked at API that Q provides and it had a method allSettled, but this method has not been exposed over the port that AngularJS uses. This is form the documentation

    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. If you want to wait for all of the promises to either be fulfilled or rejected, you can use allSettled.

    0 讨论(0)
  • 2021-02-01 16:57

    A simpler approach to solving this problem.

    $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;
    }]);
    
    0 讨论(0)
  • 2021-02-01 17:06

    just use finally

    $q.all(tasks).finally(function() {
                                // do stuff
                            });
    
    0 讨论(0)
  • 2021-02-01 17:07

    Ok, I've implemeted a basic version myself (I only want to wait for an array of promises). Anyone can extend this or create a cleaner version if they want to :-) Check the jsfiddle to see it in action: 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;
        }]);
    }]);
    
    0 讨论(0)
  • 2021-02-01 17:11

    I solved this same issue recently. This was the problem:

    • I had an array of promises to handle, promises
    • I wanted to get all the results, resolve or reject
    • I wanted the promises to run in parallel

    This was how I solved the problem:

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

    It's not a general fix, but it is simple and and easy to follow. Of course if any of your promises could resolve to null then you can't distinguish between that a rejection, but it works in many cases and you can always modify the catch function to work with the particular problem you're solving.

    0 讨论(0)
提交回复
热议问题