Как я могу расширить обещание $q в Angularjs с помощью .success и .error

Я написал этот небольшой код в пользовательском сервисе на 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;  

});

person Matohawk    schedule 28.05.2013    source источник


Ответы (3)


Вот полное решение, начинающееся с того места, где остановился @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;
  });

});
person Joshua Kifer    schedule 26.07.2013

Если вы хотите изменить поведение по умолчанию чего-то, что вводится angular, вы можете использовать decorator() метод на службе $provide.

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 для добавления функций успеха и ошибок, я пока не уверен. Но я почти уверен, что именно здесь вы хотели бы это сделать.

person jessegavin    schedule 10.06.2013

ИМХО, украшение @jessegavin для $q не идеально, оно не должно возвращать исходное обещание в функции успеха и ошибки. Он потеряет функцию выравнивания пирамиды обратного вызова.

И он не может разделить данные ответа на функцию успеха и ошибки $httpPromise дозы.

Например

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

Вот моя версия, она распознает http-ответ и вернет следующее обещание. Сделайте свой собственный $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;
});
person Eric Chen    schedule 23.04.2014
comment
Возможно, мой мысленный JS-движок дает сбой, но мне кажется, что это будет вести себя иначе, чем HttpPromise. HttpPromise должен вернуть исходное обещание, где это вернет новое обещание разрешить цепочку. - person vossad01; 27.01.2015