Introduction
Usually, jQuery function ajax
is used to make an Ajax call. Function ajax
can only make one Ajax call. A callback function is executed when the Ajax call is successful. Optionally, another callback function is called when the Ajax call returns an error. However, this function can not make multiple Ajax requests and register callback functions based on the outcome of these requests. One scenario is that a web page makes multiple Ajax requests to gather data for different sections of the page while disabling user interaction. The page enables user interaction only after the page gets all data. This article describes a method provided by jQuery to register a callback function based on multiple Ajax requests. This method is based on the concept of Deferred object.
Using the code
First, two ASP.NET Web API functions are used to accept server Ajax calls.
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public string Get(int id)
{
string s = "Id is " + Convert.ToString(id);
return s;
}
}
To make a single Ajax call, jQuery function ajax
can be used. Here is a simple example.
$.ajax({
url: 'api/values',
dataType: 'json',
type: 'GET',
success: function (data) {
alert(data[0]);
},
error: function () {
alert('Error!');
}
});
The success and error callback functions are waiting for a single Ajax call.
The jQuery library provides a way to make any callback function waiting for multiple Ajax calls. This method is based on an object called Deferred.
A Deferred object can register callback functions based on whether the Deferrred object is resolved or rejected. Here is an example of the Deferred object.
var def1 = $.Deferred();
def1.done(function (data) {
alert(data);
});
def1.resolve('Resolved!');
var def2 = $.Deferred();
def2.fail(function (data) {
alert(data);
});
def2.reject('Rejected!');
Note the Deferred object can be chained. Here is an example of the Deferred object chain.
var def = $.Deferred();
def.done(function (data) {
alert(data);
}).fail(function (data) {
alert(data);
});
def.reject('Rejected!');
The Deferred object has a method called promise
. It returns a Promise object. A Promise object exposes a subset of Deferred methods to prevent its state to be changed. That means, to prevent the Deferred object to be resolved or rejected manually. A Promise object exposes the following Deferred methods:
then
, done
, fail
, always
, pipe
, progress
, state
and promise
.
It does not expose the following Deferred methods:
resolve
, reject
, notify
, resolveWith
, rejectWith
and notifyWith
.
A Promise object can be treated as a Deferred object which state cannot be manually changed.
The jQuery ajax
function returns a jqXHR object. There are two important facts about this jqXHR object.
First, a jqXHR object is a superset of XMLHTTPRequest object. For example, a jqXHR object can query the state of the XMLHTTPRequest by referring its readyState property. If its readyState is 4, the Ajax request is completed.
Second, a jqXHR object implements the Promise interface and exposes all Promise methods. Basically, a jqXHR oject can be treated as a Promise object. For example, the done
method can be used as the success callback function for a jqXHR object.
The jQuery library provides a function called when
to accept multiple Deferred objects and returns a Promise object. The returned Promise object will be resolved when all Deferred objects are resolved. It will be rejected when any Deferred object is rejected. The Deferred objects passed to function when
can be Deferred objects, Promise objects or jqXHR objects.
Here is an example of code waiting for multiple Ajax requests.
$(document).ready(function () {
var j1 = $.ajax({
url: 'api/values',
dataType: 'json',
type: 'GET'
});
var j2 = $.ajax({
url: 'api/values',
dataType: 'json',
data: { id: 5 },
type: 'GET'
});
$.when(j1, j2).then(function (a1, a2) {
alert('Both j1 and j2 succeedeed!');
}, function (jqXHR, textStatus, errorThrown) {
var x1 = j1;
var x2 = j2;
if (x1.readyState != 4) {
x1.abort();
}
if (x2.readyState != 4) {
x2.abort();
}
alert('Either j1 or j2 failed!');
});
});
First, two variables store the jqXHR objects returned from two jQuery Ajax calls. Then the two objects are passed to function when
. The Promise object returned from when
is chained to function then
. Function then
adds handlers for the Promise object. The first parameter of function then
is a success function which is called when the Promise is resolved. The second parameter of function then
is a fail function which is called when the Promise is rejected. The fail function checks the state of each Ajax call. If the Ajax call is not completed, it is aborted. Note the fail function can still access variable j1 and j2 which are out of scope since the document ready function has been
executed and closed. This is because of JavaScript closures. A closure is an inner function which has access to variables in outer function even the outer function is closed. In other words, the inner function can keep the environment in which it is first defined.