Angular $q catch 块已解决承诺?

Angular $q catch block resolves promis?

在过去的几天里,我阅读了很多处理 promises 的最佳实践。大多数帖子的一个中心点是这样的:

So if you are writing that word [deferred] in your code [...], you are doing something wrong.1

在尝试错误处理期间,我看到了一个对我来说意想不到的行为。当我将 promise 和 It 运行 链接到第一个 catch 块时,第二个 promise 得到解决并且没有被拒绝。

问题

例子

在此示例中,您会看到 'I am here but It was an error'

Full Plunker

function BaseService($http, $q) {
  this.$http = $http;
  this.$q = $q;
}

BaseService.prototype.doRequest = function doRequest() {
  return this.$http({
      method: 'GET',
      url: 'not/exisint/url'
    })
    .then(function (response) {
      // do some basic stuff
    })
    .catch(function(response) {
      // do some baisc stuff e.g. hide spinner
    });
}


function ChildService($http, $q) {
  this.$http = $http;
  this.$q = $q;
}

ChildService.prototype = Object.create(BaseService.prototype);

ChildService.prototype.specialRequest = function specialRequest() {
  return this.doRequest()
    .then(function (response)  {
      alert('I am here but It was an error');
    })
    .catch(function (response) {
      // do some more specific stuff here and
      // provide e.g. error message
      alert('I am here but It was an error');
      return response;
    });
}

解决方法:

使用此解决方法可以解决此问题,但您必须创建一个新的延迟。

BaseService.prototype.doRequest = function doRequest() {
  var dfd = this.$q.defer();

  return this.$http({
      method: 'GET',
      url: 'not/exisint/url'
    })
    .then(function (response) {
      // do some basic stuff
      dfd.resolve(response);
    })
    .catch(function(response) {
      // do some basic stuff e.g. hide spinner
      dfd.reject(error);
    });
}

您的解决方法几乎是正确的,您可以将其简化为以下内容:

BaseService.prototype.doRequest = function doRequest() {  
  return this.$http({
      method: 'GET',
      url: 'not/exisint/url'
    })
    .then(function (response) {
      // do some basic stuff
      return response;
    }, function (error) {
      return this.$q.reject(error);
    });
}

$q.reject 是创建立即被拒绝的延迟的快捷方式。

  1. 是的,这也是其他库中的默认行为。 .then.catch 只是将 return 值包装到一个新的承诺中。您可以 return 拒绝承诺以使 .catch 链工作。

你也可以反其道而行之,例如当你出于任何原因想要拒绝成功回调中的承诺时:

function getData() {
   return this.$http.get(endpoint).then(result => {
      // when result is invalid for whatever reason
      if (result === invalid) {
         return this.$q.reject(result);
      }

      return result;
   }, err => this.$q.reject(err));
}

getData().then(result => {
   // skipped
}, error => {
   // called 
});
  1. 见上例

只是为了添加到 答案和解决方法中,您还可以将代码包装到 $q 构造函数中:

BaseService.prototype.doRequest = function doRequest() {
   return $q(function (resolve, reject) {
      $http.get('not/exisint/url').then(function (response) { // success
          /* do stuff */
          resolve(response);
      }, function (error) { // failure
          /* do stuff */
          reject(error);
      });
   });
};