An introduction to jQuery Deferred / Promise and the design pattern in general
I gave an introductory talk a while back at the London Ajax User Group about jQuery Promises after which there was a lively debate, so I thought it would be great to post the content of the slides with some notes as a sort of tutorial.
The original presentation is on Github (made with Keydown as presentation engine which doesn’t seem to handle resizing well enough to be embeddable): daaain.github.com/jquery-deferred-intro/jquery-deferred-intro/slides.html
A simple CORS AJAX example
As a simple scenario to optimise I set up a not very useful example of loading 2 articles from HTML5Rocks and showing the first sections of each.
So in which cases are Promises useful?
AJAX request handler spaghetti
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Timing issues (DOM ready, animations, etc)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
But what are these Deferreds and Promises really?
Deferred
- A proxy for an asynchronous, future event
- Has an interface for getting
resolve()d orreject()ed - Starts in
pendingstate, can only be finished once - Calls listeners immediately (but always async) once resolved
Promise
- Allows listening and state inspection (using
state()), but completely immutable so no interface for resolution - Basic (jQuery specific) listeners are
done()andfail() - Can be chained with
then()(used to bepipe()) - Can be grouped and processed using
$.when()
How do they work?
A canonical Deferred example
Setting up a listener and triggering it with resolve:
1 2 3 4 5 6 7 | |
This also works as it doesn’t matter if a Deferred is already resolved it will still trigger the callback we attach to it:
1 2 3 4 5 6 7 | |
A canonical Promise + When example
Return a Promise from a method and attach a listener to it (can have more than one):
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
So let’s untangle our example
Create a Promise for DOM ready and the two AJAX requests and wait for all of them to be fulfilled:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Another solution
The AJAX request can be already fired off while we wait for the DOM (also showing how can we chain listeners with then()):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Dealing with rejection
When a Promise gets reject()ed it will immediately cascade down the then() chain so you only need to handle it at the end (with jQuery it’s only since 1.8+ though).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
I’ve taken this example from Domenic Denicola’s blog post “You’re Missing the Point of Promises” which is a great next step on the path of understanding Promises and asynchronous control flows. Go and read it now!
A few more pointers
- In case there’s a long request you can send updates to a
progress()listener usingnotify() - You can insert transformations into the
then()chain
1 2 3 | |
- And finally it must be said that jQuery Deferred is by far not the only one, see Promises/A spec and the clarified Promises/A+ spec. If you’re not already using jQuery then Q, rsvp.js or when might be better alternatives with seamless interoperability due to stricter adherence to the CommonJS specs.