Multiple Simultaneous Ajax Requests (with one callback) in jQuery

Chris Coyier //

Let's say there is a feature on your website that only gets used 5% of the time. That feature requires some HTML, CSS, and JavaScript to work. So you decide that instead of having that HTML, CSS, and JavaScript on the page directly, you're going to Ajax that stuff in when the feature is about to be used.

We'll need to make three Ajax requests. Since we don't want to show anything to the user until the feature is ready to go (plus they all kinda rely on each other to work right) we need to wait for all three of them to be complete before proceeding.

What's the best way to do that?

Ajax calls in jQuery provide callbacks:

$.ajax({
  statusCode: {
    url: "/feature",
    success: function() {
      // Ajax success
    }
  }
});

Or the "Deferred" way, this time using a shorthand $.get() method:

$.get("/feature/").done(function() {
  // Ajax success
});

But we have three Ajax requests we're needing to perform, and we want to wait for all three of them to finish before doing anything, so it could get pretty gnarly in callback land:

// Get the HTML
$.get("/feature/", function(html) {

  // Get the CSS
  $.get("/assets/feature.css", function(css) {
    
    // Get the JavaScript
    $.getScript("/assets/feature.js", function() {

       // All is ready now, so...

       // Add CSS to page
       $("<style />").html(css).appendTo("head");

       // Add HTML to page
       $("body").append(html);

    });

  });

});

This successfully waits until everything is ready before adding anything to the page. So by the time the user sees anything, it's good to go. Perhaps that makes some of you feel nauseated, but I've done things that way before. At least it makes sense and works. The problem? It's slow.

One request ... wait to be done ... another request ... wait to be done ... another request ... wait to be done ... go.

It would be faster if we could do:

All three requests in parallel ... wait for all three to be done ... go.

We can use a bit of Deferred / Promises action to help here. I'm sure this is some JavaScript 101 stuff to some of you but this kind of thing eluded me for a long time and more complex Promises stuff still does.

In our simple use case, we can use jQuery's $.when() method, which takes a list of these "Deferred" objects (All jQuery Ajax methods return Deferred objects) and then provides a single callback.

$.when(

  // Deferred object (probably Ajax request),

  // Deferred object (probably Ajax request),

  // Deferred object (probably Ajax request)

).then(function() {

  // All have been resolved (or rejected), do your thing

});

So our callback-hell can be rewritten like:

$.when(
  // Get the HTML
  $.get("/feature/", function(html) {
    globalStore.html = html;
  }),

  // Get the CSS
  $.get("/assets/feature.css", function(css) {
    globalStore.css = css;
  }),

  // Get the JS
  $.getScript("/assets/feature.js")

).then(function() {

  // All is ready now, so...

  // Add CSS to page
  $("<style />").html(globalStore.css).appendTo("head");

  // Add HTML to page
  $("body").append(globalStore.html);

});

Another use case: mustard cutting

My use-case example above is a 5% feature. Keep the page lighter for the 95% of users who don't use the feature, and have it be a relatively quick add-on for those that do.

Another situation might be a cut-the-mustard situation where you add in additional features or content to a page in certain situations, as you decide. Perhaps do a matchMedia test on some media queries and determine the device's screen and capabilities are such that you're going to include some extra modules. Cool, do it up with some parallel Ajax calls!