【问题标题】:Make $q.all fail silently when one promise is rejected当一个承诺被拒绝时,让 $q.all 静默失败
【发布时间】:2014-09-02 19:34:40
【问题描述】:

我正在对一些地址进行地理编码,有时其中一些会失败。我希望能够获得其余结果并忽略失败的结果,以便我可以在地图上显示其他坐标。目前 $q.all 会在一个被拒绝时调用errorHandler,所以我会丢失其他promise的结果。

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

【问题讨论】:

  • 如果失败而不是拒绝,您可以使用 null 或无值(未定义)进行解析,这样您将获得相应失败值为 null 的坐标数组。

标签: javascript angularjs promise


【解决方案1】:

Interrobang 建议的解决方案很好(减去错误),但如果你不喜欢装饰器影响代码中的每一个承诺,你可以得到类似于 allSettled 的东西,如下所示:

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

【讨论】:

  • 我不喜欢语义,所以很高兴看到一个更干净的版本。但我确实喜欢简单,而且这实际上解决了上述问题。 +1
  • 我不确定这里的语义如何更好——这是一个丑陋的解决方案,类似于臭名昭著的“catch all”,即“抑制所有可能出错的事情”——如果网络请求没有'不起作用(但服务器上可能有坐标)没有办法在这段代码中处理它。这段代码解决了“解决”问题的一种非常狭窄的形式——它可以在 OP 的情况下工作,而且相当简单,但在“解决”一系列承诺的一般情况下它不会很好地工作。
【解决方案2】:

你想要的是q.allSettled,它没有在 Angular 中实现。

这是一个 relevant GitHub issue 请求实现它。

这里是 one possible implementation,称为 allComplete,您可以将其添加到您的 Angular 应用程序中:

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

【讨论】:

  • 这看起来有一个很大的错误 - 响应中的承诺顺序无法保证。当然你的意思是responses[index] = result 而不是responses.push(result) 对吧?
  • “我刚刚发布了代码,我不知道它是如何工作的”的概念让我感到恐惧。抱歉,如果您在这里发布代码,请自行承担责任,如果有错误,请修复它们。
【解决方案3】:

您可以仅使用 Angular 的实现来解决您的问题,您需要做的是隔离其他函数中的异步调用,该函数将返回一个 Promise,并且该 Promise 将为您管理。

举个例子,假设你有四个异步调用,每个函数调用一个具有授权控制的端点,如果用户没有所有权限,调用将失败

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

由于 $q.all 需要如果之前的调用之一由于用户没有权限而失败,则所有承诺都令人满意地完成整个调用将失败,但也许你对调用何时失败感兴趣返回一个空数组或默认对象,因此您必须自己处理此行为,为此您必须创建一个函数,该函数负责调用 $http.get 服务并管理如何一旦调用失败或成功,promise 将被返回,对于这个例子,让我们调用该函数 getCatalogPromise 并且在它的参数中将是服务的 URL,代码将是:

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

如果您注意 getCatalogPromise 的代码,那么返回对我们延迟的服务的调用将始终处于解析状态并不重要,这就是 $q .all 想要,唯一的区别是如果服务失败我们返回一个空数组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 2018-05-09
    • 2017-03-28
    相关资源
    最近更新 更多