Get More From jQuery Promises
Note: jQuery promises are probably not what you want to use at this point (January, 2016). Look into ES2015 promises or libraries that conform to that spec.
Intro: Promises and deferred objects were introduced to jQuery way back in version 1.5 as part of a re-write of the AJAX module. I’ve only started using them in earnest in the past few months while building LMS, but they’ve become essential to keeping asynchronous code readable and maintainable. There are already a lot of resources available on this subject, but here I’ve included all of the useful details I picked up from different places while learning the ins and outs of using jQuery’s promise implementation.
What Is A Promise Worth?
A promise represents the result of a single execution of a task that will complete at a time unknown to the caller. It could complete immediately, in the future or it could have already been completed. In code a promise object is used to register callbacks to be executed when the state of that task changes and to manage higher-level workflow, but never to change the state of the task.
Promises From jQuery
Since promises were introduced as part of the AJAX re-write the go-to example is the
$.ajax family of methods which now return promises in addition to the original API of accepting success and failure methods in the configuration object:
It looks a little cleaner, but what more can we do with a promise object?
If a promise method like
then returns another promise, so you can chain successive calls to form a descriptive timeline of tasks:
The promise API also includes a function called
pipe which was originally different from
then for “pre-filtering” the results of a promise, but as of 1.8
then === pipe, as discussed on Stack Overflow.
In any case, using
then is much cleaner than it would have been with configuration objects and nested callbacks, but we can take this further.
What if we have multiple task-based dependencies, but they don’t depend on each other?
Now we’re getting somewhere! Coordination of multiple asynchronous tasks would otherwise be difficult to write, difficult to read and difficult to debug, but by using promises everything is clear and concise.
At some point you will want to conditionally include promises in the parameter list to
when. One way to do this is to add promises to an array and then use
function.apply to match the method signature:
This will work, but has a few downsides:
- It’s an awkward expression to remember and repeat
- It’s no longer immediately clear what we’re waiting on
- If you’re expecting results from these promises they will now be given to the callback in an unpredictable order
The first problem can be mitigated by writing a function that does this directly like Q’s
When you pass an object that isn’t a promise to
when (determined by the existence of a
promise function on the object) it’s interpreted as being an immediate result that will be passed through to
then just like a successful promise result. By taking advantage of this behavior we can maintain clarity and avoid adding another method to the API:
Making Your Own Promises
Now that we know how to use promises, let’s make some of our own using deferred objects.
A deferred object can do all that a promise can plus change the state of the task. You won’t see jQuery return a deferred object from anywhere but the factory method
$.Deferred because the state of a task should only be changed by the code that has implemented that task.
Once we’ve created our deferred object, there are three methods that you’ll likely need to use:
resolve- mark this task as having completed successfully, optionally passing values
reject- mark this task as having failed, also with optional values
promise- get the more targeted promise object for attaching handlers to state changes
Here they are in a quick example:
If you want even more control over how the success or failure methods are called,
rejectWith set the first parameter as the context for executing those functions, meaning that within the callback
this will point to the object you provide instead of the deferred object.
There’s one more feature of jQuery’s promises that seems useful, but I haven’t seen used much: progress notifications. Deferred objects have a
notify method (and
notifyWith as above) that will send data to
Now let’s put everything together:
That’s promises in a nutshell. Remember that they can be used for more than coordinating network related code: you can model workflows like checkout processes, manage complex DOM interactions like animations, and more. I hope this introduction has given you the tools to appreciate and use promises in your own code.