For awhile, the standard way of making an AJAX call using jQuery is rather simple — by using the $.ajax() function:
Looks easy peasy, but there are several drawbacks to this method:
- Excessive nesting. Functions and events dependent on the returned AJAX data has to be wrapped in the success handler, because AJAX is by nature, asynchronous. Along the same line, this reduces the readability of your code.
- Difficulty in chaining and compounding. It is difficult and overwhelmingly complex to evaluate outcomes of multiple AJAX requests — we are unable to predict how long does it take for all AJAX requests (each running asynchronously) to return a response.
- Deprecation warning. As of v1.8 and above, jqXHR.success(), error and complete callbacks have been officially deprecated.
Promises and deferred objects
Thankfully, promises and deferred objects are implemented natively in the $.ajax() method:
- .done() as a replacement for .success()
- .fail() as a replacement for .error()
- .always() as a replacement of .complete()
As per other native jQuery methods, you can chain them, i.e.:
Chaining works, just as usual
Even better: assign the $.ajax() method to a variable, for which you can chain promise callbacks to. Chaining has always been a hallmark of jQuery, which allows one to reuse a cached selector, for example. In this case, the $.ajax() method returns a promise object natively, which we can use for chaining, too:
See that how the .done() callback is on the same level as the AJAX call itself, without requiring complicated nesting within the call per se? While this advantage may seem trival on a cursory glance, it greatly improves the readability of your code, especially in a production environment where your code is likely to be cluttered with multiple asynchronous calls.
It also allows you to inject code between the AJAX call itself and resolving the promise delivered by the call, allowing for greater flexibility in coding.
In other words, you can see that promises and deferred objects, implemented since jQuery v1.5 onwards, in relation to synchronicity of code execution, is akin to event bubbling in the DOM, listened on by the .on() method.
Multiple AJAX calls
Compounding several AJAX calls has never been made easier with promises. All you have to do is to listen to their status via $.when().
Dependence chain of AJAX requests
You can also chain multiple AJAX request — for example, when the second AJAX call relies on returned data on the first call. Let’s say the first call retrieves the session ID of a user, and we need to pass that value off to a second script. Remember that $.then() returns a new promise, which can be subsequently passed to the $.done() or even another $.then() method.
Modularizing AJAX requests
More often than not, one might want to modularize their code and delegate a single function to make dynamic AJAX requests. How should we invoke the AJAX call individually, and access the promise returned, in this case? It turns out to be beyond simple: simply return the AJAX object after you make a request.
Let’s say we want to make two AJAX calls, which basically uses the same parameters except for the data and url parameters:
No more messy nesting, and no more the need to repeat the $.ajax() method over and over again.
Handling arrays of returned deferred objects
Sometimes you would want to use $.when() on an array of deferred objects. An example situation would be: making a series of AJAX calls (number of calls dynamically changes, perhaps?), and then checking when all of them are done. How does that work?
There are two options:
- The easier to understand method would be to construct an empty array, use $.each() to make AJAX calls iteratively and then push the returned promise into the array.
- The preferred method (personally) would be to use .map() to construct an object containing returned promises, which we then use .get() to return an array
After that it’s all easy: simply use $.when.apply($, array) to evaluate all AJAX calls performed:
You can see that using promises and deferred object offers an unparalleled advantage over nesting jqXHR.success callbacks when making iterative AJAX calls that should be evaluated as a whole.
Of course, the aforementioned promises and deferred objects can also be used for $.get() and $.post() methods, which are basically reduced shorthand functions for the $.ajax() method using the GET or POST methods.
This writing is inspired by my visits to StackOverflow, where occurrence of jQuery-based AJAX-related questions is rather common.