0

I'm trying to add a utility method to attach notify listeners to Angular's $q promises, which are not provided by default for some reason. The intention is to provide an .update method that is chainable, similarly to the existing API:

myService.getSomeValue()
  .then(function() { /* ... */ })
  .catch(function() { /* ... */ })
  .update(function() { 
      // do something useful with a notification update
  });

Guided by an answer in Get state of Angular deferred? , and seeing from the Angular documentation for $q as well as the source code that catch is simply defined as promise.then(null, callback), I've implemented this config block:

.config(['$provide', function($provide) {
  $provide.decorator('$q', function ($delegate) {
    var defer = $delegate.defer;
    $delegate.defer = function() {
      var deferred = defer();

      deferred.promise.update = function(callback) {
        return deferred.promise.then(null, null, callback);
      };
      return deferred;
    };
    return $delegate;
  });
}]);

Which kind of works, but it seems like the above decorator doesn't get set up immediately which breaks the chaining interface. The first time a $q.defer() is defined (maybe per block?)

first.promise
  .then(function() { /* ... */ })
  .update(function() { 
      // do something useful with a notification update
  });

throws a TypeError: first.promise.then(...).update is not a function.

Example here: http://plnkr.co/edit/5utIm0HXpIKsjsA4H9oS

I've only noticed this when I was writing a simple example, I've used this code without issue when the promises were returned from a service and other promises had already been used (if this maybe would have an impact?). Is there any way to get the plunker example to work reliably when chaining immediately?

Community
  • 1
  • 1
orbitbot
  • 728
  • 6
  • 18

1 Answers1

1

Deferred and Promise are two different APIs in Angular, and this way only the promise belonging to defer is being decorated, while the promise returned from then is not.

Both of them use Promise() constructor which isn't exposed anywhere on $q. However, Promise.prototype can be modified with

   Object.getPrototypeOf(deferred.promise).update = function(callback) { ... };
Estus Flask
  • 206,104
  • 70
  • 425
  • 565