Working with jQuery’s AJAX, Promises and Deferred objects

jQuery’s implementation of making AJAX calls is quite easy to understand. There is $.ajax(), $.get(), $getJSON(), $.post() – which are all xhr requests, just different ways of writing it. It probably has the most straightforward syntax available and that’s why developers continue to use it, more than other libraries.

The funny thing is, once we get to know it, we almost want to do everything via AJAX. From fetching and posting data to grabbing static files and compiling templates, – you name it. Before you know it, we’re making AJAX calls all over the place. It can get ugly really fast. Without knowing Promises and Deferred, our code syntax will get messy.

For instance, you’ve probably run into this problem at one point.
Notice our “results” array is empty after we run our function:

function getData (){
   $.get('/page.php',function(data){
       console.log(data) //filled!
       return data;
   });
}
var results = getData();
console.log(results); //empty!

The issue here is our getData() method continues to run even before our AJAX call finishes. So naturally, our results will log empty. One thing we DON’T want to do is to continue our logic inside the $.get() utility!

Enter Promises

jQuery have promises implemented with their AJAX methods. In a nutshell, they are utilities that allow us to work with events that have completed or put them in queues or chain them – all of that good stuff.  In our case, we need a “promise“. This allows us to interact with our AJAX requests – well outside our $.get() utility. So we can continue with our logic as designed.

So let’s solve our issue with .done() – which is a method that comes with $.get():

function getData (){
   return $.get('/page.php');
}
getData().done(function(data){
  console.log(results); //filled!
});

As you can see, we can use our function getData() – anywhere in our code as we’ve intended it to be. Note that there is also .fail(), .always()  – which are the other methods for our $.get() promise object. .fail() for – when our call fails, .always() occurs every time it runs. The syntax is very self-explanatory.
Now we can also do this:

getData().fail(function(error){
  console.log(error); //some error handling
}).always(function(){
  //something that happens every time...
});

Which may serve as a global error handler for our getData() function.

You can also do both!

function getData (){
   var ajax = $.get('/page.php',function(data){
     //do something with data here
   });
   return ajax;
}
getData().done(function(data){
  //do something else with data here
});

So our method getData() – will always run our success function inside $.get(), while we use .done() – everywhere else in our code.

Multiple AJAX calls

What about multiple (different) AJAX calls? Something like below:

function getData1 (){
   return $.get('/page1.php');
}
function getData2 (){
   return $.get('/page2.php');
}
function getData3 (){
   return $.get('/page3.php');
}
getData1().done(function(data){
  //do something here
});
getData2().done(function(data){
  //do something here
});
getData3().done(function(data){
  //do something here
});

This is fine, but it looks rather clumsy don’t you think. There is a method called .when() – which deals with stuff like this.

Instead of writing multiple .done(), we can elegantly compile them into a single block. We still get access to the same objects – as if we’re dealing with each AJAX call individually.

function getData1 (){
   return $.get('/page1.php');
}
function getData2 (){
   return $.get('/page2.php');
}
function getData3 (){
   return $.get('/page3.php');
}
$.when(getData1(),getData2(),getData3()).done(function(r1,r2,r3){
   console.log(r1) //[data, status, xhrObj]
   console.log(r2) //[data, status, xhrObj]
   console.log(r3) //[data, status, xhrObj]
})

Note that each response returns an array. So for example you want to access the data of the first response, you do a r1[0] and so forth.

On a side note, I've really enjoyed the videos from Laracasts.com. These are high quality web developer tutorials on topics and technologies ranging from Laravel, React, Vue and many more. Head over to Laracasts and check them out!

Multiple Dynamic AJAX calls

Now this one is tricky. It has been asked in StackOverflow – which has an answer I would have not guessed. First thing is to get our requests in an array format. So this will be the dynamic part and it’s up to you how you want to design that. Now, $.when is smart enough to process the calls – if you pass them in single or an array.

var requests = [ //this will be the dynamic part
  $.get('page1.php'),
  $.get('page2.php'),
  $.get('page3.php')
]
$.when.apply($,requests).done(function(){
  console.log(arguments); //array of responses [0][data, status, xhrObj],[1][data, status, xhrObj]...
})

But note that we use .apply after $.when This is so we can have access to a special variable called “arguments”. The arguments hold the responses from our array of calls, in a multi-dimensional array format.

As you can see, our promise is still in-tact and does it’s thing for dynamic AJAX calls.

$.Deferred()

As the method implies, $.Deferred() is to put in on hold, until you’re ready to interact with it. $.Deferred() is useful when we have actions such as multiple AJAX calls and we want to continue interacting with them at a later time.

Building on our example, let’s wrap our $.when() inside a function so we can call it later. Let’s use the .resolve() function of $.Deferred to illustrate:

function getData1 (){
   return $.get('/page1.php');
}
function getData2 (){
   return $.get('/page2.php');
}
function getData3 (){
   return $.get('/page3.php');
}
function defCalls(){
   var def = $.Deferred();
   $.when(getData1(),getData2(),getData3()).done(function(){
     setTimeout(function(){
       def.resolve();
     },2000)
   })
   return def.promise();
}
defCalls();//returns the promise object

You see that we are delaying our .resolve by 2 seconds using setTimeout(). You will see our def.promise() will take 2 seconds to return.

Also, now we have an accessible defCalls() promise object that we can use at a later time:

defCalls().done(function(){
  //do something here
})

You can also pass parameters to .resolve() – for later use in our defCalls() promise object. For example, the responses that came back from our call:

function defCalls(){
   var def = $.Deferred();
   $.when(getData1(),getData2(),getData3()).done(function(r1,r2,r3){
     def.resolve(r1,r2,r3);
   })
   return def.promise();
}
defCalls().done(function(d1,d2,d3){
   //now we have access to data...
})

What about multiple dynamic calls? We already learned about .apply(). We simply do the same thing so we can pass “arguments”:

function defCalls(){
   var def = $.Deferred();
   var requests = [
     getData1(),
     getData2()
   ]
   $.when.apply($,requests).done(function(){
     def.resolve(arguments);
   })
   return def.promise();
}
defCalls().done(function(arr){
   //now we have access to array of data
   console.log(arr);
})

Lastly, let’s just say we want to create an action for failed events, we use the .reject() method for that:

function defCalls(){
   var def = $.Deferred();
   $.when(getData1(),getData2(),getData3()).fail(function(){
     def.reject();
   })
   return def.promise();
}
defCalls().fail(function(){
   //do something here
})

As you can see, there many benefits in using promises and deferred objects – especially in asynchronous programming with jQuery’s AJAX. Not only that it will make your code easier to read. But it also makes it easier to debug, well factored and organized – the way jQuery intended it to be.

I would like to read comments on how you’re using these objects in your own code. Please leave them below.

affiliate link arrowVivaHR AI Learning

22 Comments

  1. thank you good tutorial, very well explained with code snippets.
    I request you to write more live example for this, by using webservice,twitter,or any

    Reply
  2. Hi Michael,
    I think instead of writing this:
    function defCalls(){
    var def = $.Deferred();
    var requests = [
    getData1(),
    getData2()
    ]
    $.when.apply($,requests).done(function(){
    def.resolve(arguments);
    })
    return def.promise();
    }
    defCalls().done(function(arr){
    //now we have access to array of data
    console.log(arr);
    })

    you can simmply return the $when.apply() like so:
    function defCalls(){
    var requests = [
    getData1(),
    getData2()
    ];
    return $.when.apply($,requests);
    }
    defCalls().done(function(){
    //now we have access to array of data
    console.log(arguments);
    })

    Reply
  3. f1(function (result) {
    console.log("calling f1");
    });
    var f1 = function (handler) {
    $.ajax({
    type: "GET",
    url: service("some/Path1")),
    contentType: "application/json; charset=utf-8",
    success: function (result) {
    console.log("success for f1");
    handler(result);
    }
    });
    };
    f2(function (result) {
    console.log("calling f2");
    });
    var f2 = function (handler) {
    $.ajax({
    type: "GET",
    url: service("some/Path2")),
    contentType: "application/json; charset=utf-8",
    success: function (result) {
    console.log("success for f2");
    handler(result);
    }
    });
    };
    f3(function (result) {
    console.log("calling f2");
    });
    var f3 = function (handler) {
    $.ajax({
    type: "GET",
    url: service("some/Path3")),
    contentType: "application/json; charset=utf-8",
    success: function (result) {
    console.log("success for f3");
    handler(result);
    }
    });
    };

    how would you suggest calling f1, then f2, and f3 in order?

    Reply
    • If you look at the post above – there’s a section called “Multiple AJAX calls”. In your case, you should return the $.ajax() object so you can get the .done() method. You can also do them async – if you like.

      Reply
  4. Good article but I’m still having trouble with the following concept.
    function getArticle(articleid) {
    return $.get(‘foo.php?articleid=’+articleid)
    .done(function(data) {
    return data;
    });
    }
    var articledata = getArticle(articlenumber);
    In this case, articledata is always “undefined”. How can I fix this so that I can synchronously get article data?

    Reply
    • you have to do something like:

      var articledata = getArticle(articlenumber);
      articledata.done(function(data){
      console.log(data); //this should have your article
      })

      Reply
  5. I’m struggling a bit. In your first example, you try to get the value into a variable and it’s undefined:
    var results = getData();
    console.log(results); //empty!
    In none of the other examples do you try to put it in a variable. Instead you either do console.log or you say “do something.” I actually want it in a variable so it can persist for future use without additional ajax calls, but even assigning the variable inside .done, it still shows up undefined if you try to access it on the next line after .done. It’s still the same old async problem.

    Reply
  6. In the example
    function getData (){
    return $.get(‘/page.php’);
    }
    getData().done(function(data){
    console.log(results); //filled!
    });
    Should not line 6 use data instead of results?

    Reply

Leave a Comment.