DotNetCurry Logo

Chain AJAX Requests with jQuery Deferred

Posted by: Suprotim Agarwal , on 7/5/2014, in Category jQuery and ASP.NET
Views: 35775
Abstract: Use jQuery Deferred and Promise to chain multiple AJAX Requests and execute them asynchronously.

Imagine a scenario where you have a bunch of functions to execute asynchronously, but each function depends on the result of the previous one. You do not have an idea when each function will complete execution. In such cases, you can write Callbacks.

Callbacks are useful when working with background tasks because you don’t know when they will complete. Here’s a prototype of callbacks in action:

This article is taken from my upcoming The Absolutely Awesome jQuery Cookbook at www.jquerycookbook.com

function A(callback) {
    $.ajax({
        //...
        success: function (result) {
            //...
            if (callback) callback(result);
        }
    });
}

And here’s a prototype of callbacks in action:

A(function () {
    B(function () {
        C()
    })
});

However this code style leads to too much nesting and becomes unreadable if there are too many callback functions. Using jQuery, the same functionality can be achieved elegantly without too much nesting. The answer lies in Deferred and Promise. Let’s cook up an example:

We have four functions A(), B(), C() and D() that will execute asynchronously. However function B relies on the result of function A(). Similarly function C relies on the result of function B and so on. The task is to execute these functions one after the other and also make the results of the previous function available in the next one.

Asynchronous requests cannot be guaranteed to finish in the same order that they are sent. However using deferred objects, we can make sure that the callbacks for each async request runs in the required order.

Observe this code:

function A() {
    writeMessage("Calling Function A");
    return $.ajax({
        url: "/scripts/S9/1.json",
        type: "GET",                    
        dataType: "json"
    });
}


function B(resultFromA) {
    writeMessage("In Function B. Result From A = " + resultFromA.data);
    return $.ajax({
        url: "/scripts/S9/2.json",
        type: "GET",
        dataType: "json"
    });
}


function C(resultFromB) {
    writeMessage("In Function C. Result From B =  " + resultFromB.data);
    return $.ajax({
        url: "/scripts/S9/3.json",
        type: "GET",
        dataType: "json"
    });
}

function D(resultFromC) {
    writeMessage("In Function D. Result From C = " + resultFromC.data);
}

A().then(B).then(C).then(D);

function writeMessage(msg) {
    $("#para").append(msg + "
"); }

Observe this important piece of code

A().then(B).then(C).then(D);

From the jQuery Documentation: Callbacks are executed in the order they were added. Since deferred.then returns a Promise, other methods of the Promise object can be chained to this one, including additional .then() methods.

And that’s what we are doing here. Every Ajax method of jQuery already returns a promise. When the Ajax call in function A completes, it resolves the promise. The function B() is then called with the results of the Ajax call as its first argument. When the ajax call in B() completes, it resolves the promise and function C() is called with the results of that call and so on. Here we are just adding return in every function to make this chain work.

By the way, you may have observed that we are repeating settings for multiple AJAX calls in the same script. You can clean up the code further by specifying global settings using $.ajaxSetup. Here’s how:

$.ajaxSetup({
  type: 'GET',
  dataType: "json",
  delay: 1
});

and then each call is reduced to

function A() {
    writeMessage("Calling Function A");
    return $.ajax({
        url: "/scripts/S9/1.json",
    });
}

Run the sample and you will get the following output:

image

Once you’ve got your head wrapped around them, jQuery Deferred can be simple yet powerful. The ability to easily chain methods is beautiful and easy on the eyes!

This article is taken from my upcoming The Absolutely Awesome jQuery Cookbook at www.jquerycookbook.com

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Stuart Hallows on Monday, July 7, 2014 6:59 PM
Unfortunately one problem with the jQuery promise implementation is that it's not too easy to handle errors. It'd be nice to be add an .error() handler on the end of the call chain and have it called if *any* of the requests fail, but that doesn't work.
There are alternative implementations of promises that do handle this case, I believe Q does: http://api.jquery.com/error/
Comment posted by Rob on Monday, September 15, 2014 2:08 PM

what if you have an "unknown" number of call backs? aka prerequisites  ...

Suppose you have an api that only allows you to modfify 1 parameter in order to gather a collection of data points ... this could vary from 10 ajax calls to 20 depending on the range of data points.

Using the approach here would entail some even weirder meta code if you have N call backs

you could write code that evals out something like:

f1().then(f2).then(f3).then(.........) but I really dislike this approach which I would do in ruby many moments ago.

looking back at the original question is when should my final result fire? ... only when the list of call backs is complete.

For this case I offer you a recursive solution:

Chart.ajax_queue = function(data_sets, array_of_urls, render) {
   if (array_of_urls.length == 0) {
      render(data_sets);              //our list of callbacks is complete we are free to display the data obtained
   } else {
     var url = array_of_urls.shift(); //grab and remove next api call
     $.getJSON(url, function(chart_data) { //execute ajax call
       data_sets[parent_key][k].push(chart_data); //append data to mutable parameter
       Chart.ajax_queue(data_sets, array_of_urls, render); //repeat the same strategy after the ajax actions are performed
     });
   }
}


data_sets: is just a collection of collections that obtain a data point f(x) for x passed in as a parameter to an API
array_of_urls: is each API url with the x variable assigned
render: is a callback that happens when the data_sets are fully complete


One might argue why not obtain all of the data in one ajax call ... and ideally this would disregard the need to make subsequent ajax calls .... however working with a legacy system has force my hand which lead me to finding this article and herby cooking up this solution.

Cheers