如何在Angularjs中使用.success和.error扩展$q promise

22
我在AngularJS的自定义服务中编写了这段小代码。
在我的服务中:
        var deferred = $q.defer();
        var promise = deferred.promise;

        deferred.resolve('success');
        deferred.reject('error');

        /* Handle success and error */
        promise.success = function(fn) {

            promise.then(function(response) {

                fn(response);

            });

            return promise;
        };

        promise.error = function(fn) {

            promise.then(null, function(response) {

                fn(response);

            });

            return promise;
        };

在我的控制器中:

        promiseService.myPromise()
            .success(function(data){

                $scope.success= data;

            })
            .error(function(data){

                $scope.error = data;

            });

我只是处理来自承诺($q服务)的成功和错误。我需要在许多其他服务中使用这段代码,因此我想用自定义方式直接扩展$q服务。

因此,我希望我的服务能够像这样:

    var deferred = myPromiseService.$qCustom.defer();
    var promise = deferred.promise;

    deferred.resolve('success');
    deferred.reject('error');

    return promise;

有什么想法吗?我找到了一些关于扩展Angularjs过滤器的解释,我的问题是找到一个好的方法来扩展$q的所有功能并添加自定义内容。

我从以下代码开始,它可以处理$q的默认行为:

angular.module('myApp').service('myPromiseService', function($q){

  $qCustom = $q;  

});
3个回答

27

这里是完整的解决方案,延续了@jessegavin的思路。

var myApp = angular.module("myApp", []);

myApp.config(function ($provide) {

  $provide.decorator('$q', function ($delegate) {
    var defer = $delegate.defer;
    $delegate.defer = function () {
      var deferred = defer();
      deferred.promise.success = function (fn) {
        deferred.promise.then(function(response) {
          fn(response.data, response.status, response.headers);
        });
      return deferred.promise;
      };
      deferred.promise.error = function (fn) {
        deferred.promise.then(null, function(response) {
          fn(response.data, response.status, response.headers);
        });
        return deferred.promise;
      };
      return deferred;
    };
    return $delegate;
  });

});

11

如果您想更改由Angular注入的某些内容的默认行为,则可以在$provide服务上使用decorator()方法。

var myApp = angular.module("myApp", []);

myApp.config(function ($provide) {
  $provide.decorator("$q", function($delegate) {
    // The $delegate argument here refers to the $q service.

    $delegate.defer = function() {
      alert("You just tried to call defer()!");
    };

    // Now, every time angular provides an instance of $q via
    // injection, it will return your customized version of $q.

    return $delegate;
  });
});

请访问http://plnkr.co/edit/RuZF2cGkVHwlu7NIhxEZ?p=preview查看上面的示例。

至于修改$q以添加成功和错误函数,我目前不确定。但我非常确定这就是您想要进行此操作的地方。


3

在我看来,@jessegavin对$q的装饰并不完美,它不应该在成功和错误函数中返回原始promise。这将失去扁平化回调金字塔的功能。

而且它不能像$httpPromise那样将响应数据分成成功和错误函数。

例如:

//can't do this..
somePromise.success(function(){
  return $http.get(...)//another primise
}).success(function(data){
  //data from $http.get..
})

这是我的版本,它将识别http响应并返回下一个promise。使您自己的$q具有与$httpPromise相同的行为。

$provide.decorator('$q', function($delegate) {
  function httpResponseWrapper(fn) {
    return function(res) {
      if (res.hasOwnProperty('data') && res.hasOwnProperty('status') && res.hasOwnProperty('headers') && res.hasOwnProperty('config') && res.hasOwnProperty('statusText')) {
        return fn(res.data, res.status, res.headers, res.config, res.statusText);
      } else {
        return fn(res);
      }
    };
  };
  function decorator(promise) {
    promise.success = function(fn) {
      return decorator(promise.then(httpResponseWrapper(fn)));
    };
    promise.error = function(fn) {
      return decorator(promise.then(null, httpResponseWrapper(fn)));
    };
    return promise;
  };
  var defer = $delegate.defer;
  $delegate.defer = function() {
    var deferred = defer();
    decorator(deferred.promise);
    return deferred;
  };
  return $delegate;
});

也许我的 JS 引擎有问题,但我觉得这段代码与 HttpPromise 的行为不同。HttpPromise 应该返回原始的 promise,而这段代码将返回一个新的 promise 以允许链式调用。 - vossad01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接