Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the New York ColdFusion User Group (Jan. 2010) with: Javier Julio
Ben Nadel at the New York ColdFusion User Group (Jan. 2010) with: Javier Julio

$q.when() Is The Missing $q.resolve() Method In AngularJS

By Ben Nadel on

In AngularJS, the $q services has a top-level method, $q.reject(), which will create a promise that is immediately rejected with the given value. Ever since I saw this method, I have always wondered where the "resolve" equivalent was? If there's a $q.reject(), why not a $q.resolve()? Well, it turns out there is. Thanks to a mental-block, I never quite made the connection; but, $q.when() is the "missing" $q.resolve() method.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

The $q.when() method doesn't just create a promise that is immediately resolved; rather, it normalizes a value that may or may not be a "thenable" object. If the given value is a promise, $q.when() will properly chain off of it. If the given value is not a promise, $q.when() will create promise resolved with the given value.

To see this in action, I've put together a tiny demo that showcases the "simple" case of $q.reject() and $q.when() to create immediately rejected and resolved promises, respectively:

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • $q.when() Is The Missing $q.resolve() Method In AngularJS
  • </title>
  • </head>
  • <body ng-controller="AppController">
  •  
  • <h1>
  • $q.when() Is The Missing $q.resolve() Method In AngularJS
  • </h1>
  •  
  •  
  • <!-- Load scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs/angular-1.2.26.min.js"></script>
  • <script type="text/javascript">
  •  
  • // Create an application module for our demo.
  • var app = angular.module( "Demo", [] );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I control the root of the application.
  • app.controller(
  • "AppController",
  • function( $scope, $q ) {
  •  
  • // The $q.reject() method creates a promise that is immediately rejected
  • // with the given reason.
  • $q.reject( "meh" ).catch(
  • function handleReject( reason ) {
  •  
  • console.log( "Rejected with reason:", reason );
  •  
  • }
  • );
  •  
  • // The $q.when() method creates a promise that is immediately resolved
  • // with the given value.
  • // --
  • // NOTE: If the given value is already a promise then it will be properly
  • // chained (and routed) by the resultant promise.
  • $q.when( "woot!" ).then(
  • function handleResolve( value ) {
  •  
  • console.log( "Resolved with value:", value );
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

When we run this code, we get the following console output:

Rejected with reason: meh
Resolved with value: woot!

As you can see, each method returned a promise. The $q.reject() promise was rejected and the $q.when() promise was resolved.




Reader Comments

You are not alone!
It took me a lot of time to figure out the best way to use $q, specially inside services.
Before $q.when() and $q.reject() I was returning some silly responses into service methods =/

Reply to this Comment

@Darlan,

Promises are generally hard to wrap your head around. And, even when you start using them, I find that I often forget how to use them properly. Every few months, I find it helpful to review this blog post on promise anti-patterns and how to avoid them:

http://taoofcode.net/promise-anti-patterns/

Some really good stuff there!

Reply to this Comment

I thought the same thing when I first started using promises. In fact I went as far as writing $q.noop before discovering $q.when.
$q.noop = function () {
var deferred = $q.defer();
deferred.resolve();
return deferred.promise;
};

I do have one gripe about $q.when though. $q.when(myFunc) will resolve immediately myFunc. Although I think this is the clearest and cleanest API, it's not the most useful for me. Instead I prefer $q.whenFn(myFunc) which invokes and resolves the return from myFunc. $q.whenFn has proven really useful for implementing lazy loading in my APIs! I wrote about $q.whenFn here: http://www.codeducky.org/anqwhenfn/

Reply to this Comment

@Steven,

There is actually no need in such noop() which doesn't accept any arguments since there is $q.resolve (not it's a property) which serves this purpose.

Reply to this Comment

I love the documentation for $q.when:

"Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted."

...specifically that last part

What exactly does this mean? If you're using a promise from an external library that uses setTimeout - would this be an example of a promise that "can't be trusted"? Or is it more that you can't trust if it's a promise or a value?

Reply to this Comment

@Jordan,

That's a really good question! One guess could be that it has to do with the way $q integrates with the $digest lifecycle. If you create a promise with $q, AngularJS will automatically trigger a $digest whenever the promise is exercised, either with reject, resolve, or notify. If, however, you get a promise from a non-AngularJS source, then you don't have that digest-integration. Using $q.when() to wrap an "untrusted" promise would be able to bridge the gap and make sure that the $digest cycle is triggered when the original promise returns with a value.

But that is just a guess on my part - it's actually something that I'd like to try to play with.

Reply to this Comment

@Ben,

Yes - exactly what I was thinking!

A good use case for this is if, for instance, you really want to use an external promise library - you want to avoid the "Forgotten Promise" anti-pattern, but the library gives you a way to declare a timeout so you can ensure your call is not forgotten.

Anyways I had a look at the code and it doesn't appear that it makes a difference whether you decide to manually create the promise via $q.defer() or use $q.when(). $q.when() appears to just be a convenience method so you don't have to write extra code:

var when = function(value, callback, errback, progressBack) {
var result = new Deferred();
result.resolve(value);
return result.promise.then(callback, errback, progressBack);
};

..and so I don't think the $digest cycle should be handled any differently.

Also, just thought I would share this fun post on Angular promises: http://andyshora.com/promises-angularjs-explained-as-cartoon.html

Reply to this Comment

@Jordan,

I just put together a follow-up exploration of this "can't be trusted" concept:

http://www.bennadel.com/blog/2835-normalizing-untrusted-deferred-promise-values-for-the-digest-lifecycle-in-angularjs.htm

If you watch the video, you will see that using the raw jQuery Deferred value will not trigger a digest; but, once it is wrapped in $q.when(), the $digest is successfully triggered.

And yes, I believe that you are wright - the .when() method is for convenience.

Reply to this Comment

Thanks for this great short info video. I really wish they'd called it resolve and not when. When is conditional, resolve would just be that... resolve it. *sigh*

Reply to this Comment

Ben, thanks for pointing this out! The angular documentation is very sparse about this and I would not have drawn the same conclusion.

Just solved an immediate problem I was facing.

Cheers

Jürgen

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.