Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

$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 =/

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/

@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.

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?

@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.

@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

@Jordan,

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

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.

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*

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