You need promises as soon as you do anything that involves an asynchronous API. In Object JavaScript – Asynchronous Programming Using Promises, you learned the basics about promises.
jQuery’s implementation of promises is based around the jQuery.Deferred object. This is a chainable constructor where you can check for the existence of a promise. The jQuery Deferred object can also invoke callback queues and pass on the success of synchronous and asynchronous functions.
Deferreds were added as a part of a large rewrite of the ajax module following the Common JS/Promises A, which says a promise—what’s returned by an originator to represent a value to deliver in the future—is an object with a function called then. Consumers subscribe to fulfillment of the promise by calling then. (Promises in Windows also support a similar function called done that’s used in promise chains.)
The default state of any Deferred object is something is unresolved. Callbacks which may be added to it through .then() or .fail() are queued up and get executed later on in the process.
Let’s begin with a simple example.
jQuery Promise Example
You call promise
on a jQuery selection, and you’ll get an object that you can bind event handlers too, when all the animations in the object have completed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$('div.special').fadeIn(5000).promise() | |
.then(function() { | |
// run when the animation succeeds | |
}).then(function() { | |
// also run when the animation succeeds | |
}); |
jQuery.ajax
So you may be wondering about whether you need to do something special for jQuery.ajax calls.
Ajax requests implement the Promise interface on the jqXHR objects returned by $.ajax(). So you receive all the properties, methods, and behavior of a Promise on the jqXHR returned value.
The following is a code example from the jQuery documentation showing how jQuery can be used to chain tasks. All of jQuery’s Ajax methods return a superset of the XMLHTTPRequest
object. This jQuery XHR object, or jqXHR, returned by $.get()
implements the Promise interface, giving it all the properties, methods, and behavior of a Promise
Here’s another example of how you can handle a post.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var post = $.ajax({ | |
url: "/echo/json/", | |
data: { | |
json: JSON.stringify({firstName: "Jose", lastName: "Romaniello"}) | |
} , | |
type: "POST" | |
}); | |
post.done(function(p){ | |
alert(p.firstName + " saved."); | |
}); | |
post.fail(function(){ | |
alert("error!"); | |
}); |
You can add as many callbacks as you want, the syntax is clear because we don’t need an extra parameter in the method.
jQuery Deferred Object
jQuery introduced a new concept in version 1.5 called Deferred which is also a derivative implementation of the CommonJS Promises/A proposal. The Deferred object exposes a then method for you to handle both the fulfillment and error states.
A Deferred is nothing more than an object that allows you to register callbacks. Calling .resolve() on a Deferred will trigger the done handlers, while calling .reject() will trigger any fail handlers.
It has two important methods:
- resolve
- reject
And it has three important “events” or ways to attach a callback:
- done
- fail
- always
Deferred in jQuery are a chainable utility object created by calling the jQuery.Deferred() method. You can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.
After creating a Deferred object, you can use any of the methods by either chaining directly from the object creation or saving the object in a variable and invoking one or more methods on that variable.
When you call resolve() on the Deferred object, any done Callbacks added by deferred.then() or deferred.done() are called. Callbacks are executed in the order they were added. Each callback is passed the args from the deferred.resolve().
So when you use deferred, you can specify when the then() and done() methods are called.
Creating Your Own Deferreds
You learned from the previous section and the previous post that $.ajax
and $.when
implement the deferred API internally, but you can also create your own implementations:
Here’s an example of a the HTML showing “waiting” while a long operation (wait function) occurs. wait() returns a promise that we set to pro variable. When called, wait() returns immediately with the promise that the result will be either resolved or rejected at some point in the future. pro.done() registers the result function that will be called when pro has been successfully completed.
Inside the wait() function, the deferred object resolves based on some logic inside the function (in this case, waiting two seconds). The wait function returns a promise that when the function is completed it will call of its done functions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$('#result').html('waiting…'); | |
var pro = wait(); | |
pro.done(result); | |
function result() { | |
$('#result').html('done'); | |
} | |
function wait() { | |
var deferred = $.Deferred(); | |
setTimeout(function() { | |
deferred.resolve(); | |
}, 2000); | |
return deferred.promise(); | |
} |
Instead of setTimeout, you can image an animation, Web worker, or other long running function.
jQuery Promise
The Deferred object has another important method named Promise(). This method returns an object with almost the same interface than the Deferred, but it only has methods attach callbacks and does not have the methods to resolve and reject.
Best Practice. A promise is something shared with other objects, while a deferred should be kept private within a function. Primarily, a deferred (which generally extends Promise) can resolve itself, while a promise might not be able to do so.
This is useful when you want to give to the calling API something to subscribe to, but not the ability to resolve or reject the deferred.
The literal answer is, a promise is something shared w/ other objects, while a deferred should be kept private. Primarily, a deferred (which generally extends Promise) can resolve itself, while a promise might not be able to do so.
The jQuery Promise object provides a subset of the methods of the Deferred object (then
, done
, fail
, always
, pipe
. isResolved
, and isRejected
) to prevent users from changing the state of the Deferred.
When you use a promise, you can still get to the deferred object. The returned Promise is linked to a Deferred object stored on the .data()
for an element.
When to Integrate “Pure” Promises
Domenic Denicola wrote the article You’re Missing the Point of Promises in which he criticizes jQuery’s implementation of Promises/A. Take for example the following code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
promise.then(fn1).then(fn2).then(fn3, fail); |
If an error is thrown in fn1, the fail function should be called. This technique to handle errors in asynchronous functions does not work in jQuery. You can try the example out in You’re Missing the Point of Promises Gist.
Instead, you may want to use “pure” Promises/A offered in JavaScript libraries, such as Q, rsvp.js, or when.js.
You can integrate jQuery promises and convert them into a “pure” promise by converting it as soon as possible. For example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var promise = Q.when($.get("https://github.com/kriskowal/q")); |
You can learn more about Q in a later post.
References
- Deferred Object reference in jQuery documentation
- jQuery.promise() in jQuery API Documentation
- José F. Romaniello excellent article Understanding JQuery.Deferred and Promise includes some great samples and an excellent conceptual walkthrough.
- Edwin Martin’s excellent post Deferred and promise in jQuery
- jQuery Deferred Objects Promise Callbacks
- Using Deferreds in jQuery 1.5
- Asynchronous Programming in JavaScript with “Promises”
- Using the jQuery Promise Interface to Avoid the AJAX Pyramid of Doom
- Creating Responsive Applications Using jQuery Deferred and Promises
One thought on “Object JavaScript – Promises for Asynchronous Operations Using jQuery”
Comments are closed.