最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Make $q.all fail silently when one promise is rejected - Stack Overflow

programmeradmin4浏览0评论

I am geocoding some addresses, sometimes some of them fail. I'd like to be able to get the rest of the results and ignore the failed one so I can display the other coordinates on a map. Currently $q.all will call the errorHandler when one is rejected, so I lose the results of other promises.

      $q.all(promises).then(function(coords) {
          for (var j = 0;j<coords.length;j+=1) {
              //success code
          }
      }, function(error) {
        console.log("Failed",error.type,error.message);
      });

I am geocoding some addresses, sometimes some of them fail. I'd like to be able to get the rest of the results and ignore the failed one so I can display the other coordinates on a map. Currently $q.all will call the errorHandler when one is rejected, so I lose the results of other promises.

      $q.all(promises).then(function(coords) {
          for (var j = 0;j<coords.length;j+=1) {
              //success code
          }
      }, function(error) {
        console.log("Failed",error.type,error.message);
      });
Share Improve this question edited Sep 2, 2014 at 19:40 Interrobang 17.5k3 gold badges57 silver badges63 bronze badges asked Sep 2, 2014 at 19:34 TomTom 1,5652 gold badges22 silver badges45 bronze badges 1
  • In case of failure instead of rejecting you can resolve with null or no value (undefined) so you will get array of coords with respective failed value as null. – PSL Commented Sep 2, 2014 at 19:42
Add a ment  | 

3 Answers 3

Reset to default 9

The solution suggested by Interrobang is good (minus the bug) but if you dislike having decorators affect every single promise in your code you can get something similar to allSettled like this instead:

var suppress = function(x) { return x.catch(function(){}); } 
$q.all(promises.map(suppress)).then(function(coords) {
     for (var j = 0; j < coords.length ; j+=1) {
          //coords[j] contains the coords on success or is undefined on failure
      }
});

What you want is q.allSettled, which isn't implemented in Angular.

Here's a relevant GitHub issue requesting its implementation.

And here is one possible implementation, called allComplete, that you can add to your Angular app:

angular.module('App.services', ['ngResource'])
  .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;
    }]);
  })
;

You can resolve your problem using only the implementation of Angular what you have to do is isolate the async calls in other function which is going to return a promise and this promise is going to be managed for you.

Let's use a example, let's say that you have four async calls and each function call an endpoint with authorization control, if the user does not have all the permissions the call is going to fail

  function getAllCatalogs() {
        return $q.all([
            $http.get(baseUrl + 'equipment-design/'),
            $http.get(baseUrl + 'engines-design/'),
            $http.get(baseUrl + 'suspension-design/'),
            $http.get(baseUrl + 'artifacts-design/')
        ]).then(function (data) {
            return data;
        });
    }

Due to $q.all needs that all promises finish satisfactorily if one of the previous calls fail due to the user does not have the permissions the whole call is going to fail, but maybe you are interested in when a call fail just return an empty array or a default object, so you have to handle this behavior for yourself, to achieved this you have to create a function which is the responsible to call the $http.get service and manage how the promise is going to be returned once the call fail or success, for this example let's call that function getCatalogPromise and in its parameters is going to be the URL for the service, the code will be:

   function getCatalogPromise(url) {
        var deferred = $q.defer();

        $http.get(url).then(function (response) {
            deferred.resolve(response)
        }, function () {
            deferred.resolve([]);
        })

        return deferred.promise;
    }

    function getAllCatalogs() {

        return $q.all([
            getCatalogPromise(baseUrl + 'equipment-design/'),
            getCatalogPromise(baseUrl + 'engines-design/'),
            getCatalogPromise(baseUrl + 'suspension-design/'),
            getCatalogPromise(baseUrl + 'artifacts-design/')
        ]).then(function (data) {
            return data;
        });
    }

if you pay attention to the code of getCatalogPromise it does'n matter what return the call to the service our deferred always is going to be in resolve state and that is what $q.all want, the only difference is that if the service fail we return an empty array.

发布评论

评论列表(0)

  1. 暂无评论