The $q
library comprises two separate APIs:
- the deferred API, which controls a promise
- the promise API, which contains functions for reporting on the success, failure or notification of an asynchronous operation.
You access the deferred API’s functions via a deferred
object, which is created for you when you call $q.defer()
. Think of this deferred
object as the driver of the promise you ultimately want to use.
One of the properties of the deferred
object is a promise
object. It’s this promise object that you use to return values from an asynchronous call, through a set of three functions that form the promise’s API:
- promise.then() – success
- promise.catch() – error
- promise.notify() – notification
The relationship between the defer object and its promise
You don’t call the promise's
functions explicitly; rather, they’re called for you by the deferred
object, which triggers one of these functions according to the action you wish to trigger, as follows:
- to return a value on successful receipt of asynchronous information, you’d call
defer.resolve()
, which, in turn, will call its promise’sthen()
function. -
if you call
defer.reject()
, the promise’scatch()
function will be called. -
call
defer.notify()
, andpromise.notify()
will be called.
The table below summarizes these functions, showing the deferred
object’s functions and the corresponding promise
functions that they call.
Deferred function | Promise function |
---|---|
defer.resolve() | promise.then() |
defer.reject() | promise.catch() |
defer.notify() | promise.notify() |
The deferred object controls the promise
What you should take from this is that although it’s the promise
object that you usually deal with most (for example, in REST calls made via $http or $resource), it’s actually the deferred
object that you use to control the promise
.
You can think of the deferred
object, therefore, as the promise’s
driver. Without the deferred
object, the promise
would just sit there waiting for a resolution or rejection that will never come.
For example:
// Create our promise’s driver var deferred = $q.defer(); // Create a reference directly to the deferred object's // promise property var promise = deferred.promise; // Wire up the promise's success and failure handlers, // so that the successHandler function is called on success, // and failureHandler is called on failure promise .then(sucessHandler) .catch(failureHandler); function successHandler(msg){ console.log(msg); } function errorHandler(err){ console.log(err); } // At this stage, the promise isn’t actually doing anything. // It’s just waiting for the deferred object to resolve or // reject it. // So let's resolve the promise... // Do something async setTimer(function(){ // Only when deferred.resolve is called will the // promise’s successHandler function be executed deferred.resolve(“Success”); }), 10000); // ...and now let's reject it... setTimer(function(){ // Only when deferred.reject is called will // the promise’s failureHandler function be // executed deferred.reject(“Failure”); }), 10000);
The standard promise pattern
The simplest pattern for using a promise, with a timer used to emulate asynchronous calls.
The advantage of this approach
You may be wondering why you need to call a function on the deferred object in order for the promise’s corresponding function to be called.
The advantage that this approach provides is that it decouples your promise
(i.e. the object that returns the result of your asynchronous call), from its control mechanism – the deferred
object. As such, you can pass the promise
around as an object that’s independent of the deferred
object – return it from a function, use it in a callback function, an event, whatever you want to do with it – and still maintain control over it from your deferred
object, which has the power to trigger the promise’s success or error handlers at will.
This is what happens in a call to Angular’s $http
and $resource
objects. Although you only handle the returned promise
and you never see the deferred
object that calls the promise’s success or failure handlers, the deferred
object is indeed there, lurking behind the scenes deep within the AngularJS source code.
Tl;dr
The defer
object contains a promise
object as a property. This promise does nothing unless it’s instructed to do so by the defer
object that contains it, via the defer's
resolve()
, reject()
and notify()
functions.
The Deferred Object’s API
$q.defer()
Use the $q
object’s defer()
function to create the defer
object and access its promise.
// Create a defer object var deferred = $q.defer(); // We can now access the defer object's promise using // 'deferred.promise' // (e.g. deferred.promise.then(function(){..})) // It's more convenient to create a variable that references // 'deferred.promise' directly, though: var promise = deferred.promise;
defer.resolve()
Use the defer
object’s resolve()
function to pass a value to the promise’s successHandler()
function.
// Pass the value 'yay!' to the // promise's successHandler function deferred.resolve(‘yay!’); promise.then(function(data){ // successHandler console.log(data); //yay! })
defer.reject()
Use the defer
object’s reject()
function to pass a value to the promise’s failureHandler()
function.
// Pass the value 'oops!' to the // promise's failureHandler deferred.reject(‘oops!); promise.catch(function(data){ // failureHandler console.log(data); //oops! })
defer.notify()
Use the defer
object’s notify
function to pass a value to the promise’s notifyHandler
function.
// update the user on the progress of an asynchronous task // that may take some time // Pass the value 'waiting...' to // the promise's notifyHandler function deferred.notify(‘waiting...’); promise.notify(function(data){ console.log(data); //waiting... });