链接 ngResource $promise 成功和错误

Posted

技术标签:

【中文标题】链接 ngResource $promise 成功和错误【英文标题】:chaining ngResource $promise success and errors 【发布时间】:2015-05-07 11:34:57 【问题描述】:

我想知道是否可以在多个级别上处理 ngResource 返回的$promise,以便代码是 DRY

这是一个简单的例子

aService = function(aResource) 
  var error, success;
  success = function(response) 
    console.log('Service Success');
  ;
  error = function(response) 
    console.log('Service Error');
  ;

  this.servicePostReq = function() 
    return aResource.save().$promise.then(success, error);
  ;
  return this;

angular.module('app.service').factory('aService', ['aResource', aService]);

到目前为止,这工作正常......当响应正常时它Service Success,当响应不正常时它Service Error

但是当我添加一个使用 aService 的控制器时,如下所示

aController = function(aService) 
  var error, success;
  success = function(response) 
    console.log('Controller Success');
  ;
  error = function(response) 
    console.log('Controller Error');
  ;
  this.controllerPostReq = function() 
    aService.servicePostReq().then(success, error);
  ;

  return this;
;

angular.module('app.controller').controller('aController', ['aService', aController]);

控制器总是成功...

所以如果请求返回成功输出是

Service Success
Controller Success

如果请求失败,输出是

Service Error
Controller Success

我如何链接承诺,这样我就不必为每个使用服务的控制器添加服务中处理的代码?

【问题讨论】:

【参考方案1】:

添加对$q的依赖并使用$q.reject来控制执行...

在您的示例中,您需要在 aService.error 方法中使用 $q.reject

如上所述here in the $q docs

reject(reason);

创建一个以指定原因被拒绝的承诺。这个 api 应该用于在一系列 Promise 中转发拒绝。如果您正在处理 Promise 链中的最后一个 Promise,则无需担心。

当将 deferreds/promises 与熟悉的 try/catch/throw 行为进行比较时,可以将拒绝视为 javascript 中的 throw 关键字。这也意味着,如果您通过 promise 错误回调“捕获”错误,并且希望将错误转发到从当前 promise 派生的 promise,则必须通过返回通过拒绝构造的拒绝来“重新抛出”错误。

【讨论】:

【参考方案2】:

为了正确链接承诺,成功和错误处理程序都应该返回一些值。返回值会自动包装在一个新的 Promise 中。这意味着在出现错误的情况下,您必须使用 $q.reject 返回一个被拒绝的承诺。

所以你的服务应该是这样的:

aService = function($q, aResource) 
  var error, success;
  success = function(response) 
    // important! return a value, handlers down the chain will
    // execute their success handlers and receive this value
    return 'Service Success';
  ;
  error = function(response) 
    // important! return a REJECTION, handlers down the chain will
    // execute their error handlers and receive this value
    return $q.reject('Service Error');
  ;

  this.servicePostReq = function() 
    return aResource.save().$promise.then(success, error);
  ;
  return this;

angular.module('app.service').factory('aService', ['$q', 'aResource', aService]);

【讨论】:

虽然我同意你的大部分回答,但我对为什么 aService.success 方法不使用 $q.resolve() 而 aService.error 使用 $q.reject() 感到困惑。 这仍然行不通。原因是因为aResource.save().$promise.then(success, error) 既是thenable 又包含一个错误处理程序。无论是否有错误,任何链接到那里的 .then 语句都将运行。 this Plunkr 中的示例(打开控制台查看输出)。【参考方案3】:

问题在于您的服务。改变这个:

  this.servicePostReq = function() 
    return aResource.save().$promise.then(success, error);
  ;

到这里:

  this.servicePostReq = function() 
    return aResource.save().$promise.then(success);
  ;

说明:

由于您的服务返回aResource.save().$promise.then(success, error),它返回了一个包含错误处理程序的新承诺。稍后,在您的控制器中,您可以像这样添加到链中。

aService.servicePostReq().then(success, error);

如果将其展开,此时完整的 Promise 链看起来:

return aResource.save().$promise
  .then(successFnFromService, errorFnFromService)
  .then(successFnFromController, errorFnFromController);

由于您使用errorFnFromService 捕获了来自aResource.save() 的错误,因此此时承诺链基本上是“干净的”,它将继续下一个then

通过删除第一个错误处理程序,您可以稍后捕获错误。

处理 Promise 链中错误的更好方法(通常)是在链的末尾使用单个 .catch()。

考虑一下这个糟糕的代码(尝试在浏览器控制台上运行):

new Promise(
  function(resolve, reject)
    reject('first');
  ).then(
    function(result) 
      console.log('1st success!', result);
      return result;
    ,
    function(err) 
      console.log('1st error!', err);
      return err;
    
  ).then(
    function(result)
      console.log('2nd success!', result);
    ,
    function(err)
      console.log("2nd error!", err);
    
  );

输出:

1st error! first
2nd success! first

更好的方法:

new Promise(
  function(resolve, reject)
    reject('first');
  ).then(function(result) 
    console.log('1st success!', result);
    return result;
  ).then(function(result)
    console.log('2nd success!', result);
  // catch error once at the end
  ).catch(function(err)
    console.log("error!", err);
  );

输出:

error! first

在浏览器控制台中尝试这两种方法,并将 reject 更改为 resolve 以查看它对输出的影响。

【讨论】:

以上是关于链接 ngResource $promise 成功和错误的主要内容,如果未能解决你的问题,请参考以下文章

[AngularJS] AngularJS系列 进阶篇之promise

如何在angularjs中链接promise错误函数

与 ngResource 相比,使用 Restangular 有啥优势?

ngResource 中的虚拟属性

我如何在 angularjs 中使用 Restful。我使用了 ngResource 但它不起作用。如果我使用 ngResource,js 文件 nt 正在执行

AngularJS ngResource 不发送自定义标头