Grow your CSS skills. Land your dream job.

Multiple Simultaneous Ajax Requests (with one callback) in jQuery

Published by 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!

Comments

  1. Ron
    Permalink to comment#

    this has been the simplest explanation of jquery deferred that I’ve found. Very nice! Thanks Chris!

  2. stefan
    Permalink to comment#

    Hey Chris, thanks for the article.

    Any reason, why do you prefer to put fetched results in global scope?

    $.when(
      // Get the css-tricks once
      $.get("http://css-tricks.com"),
    
      // get it twice
      $.get("http://css-tricks.com")
    ).then(function( csstricks1, csstricks2 ) {
      console.log( 'Fetched css tricks' );
      console.log( csstricks1, csstricks2 );
    });
    

    Works also fine. :)

    • That’s probably better and perhaps even more comfortable for people converting. I tried something like that and couldn’t quite get it working but I’m sure I just screwed it up.

    • So the then callback takes the results of the deferred objects as arguments. Interesting and helpful. Thanks for the tip!

  3. Petr
    Permalink to comment#

    I was searching this for a while, but without success. All explanations were so difficult. Thank you!

  4. Permalink to comment#

    Nice article! I really like to use promises to avoid callback-hell.
    But why the “globalstore”-object?

    You could just do:

    $.when(
      // Get the HTML
      $.get("/feature/"),
    
      // Get the CSS
      $.get("/assets/feature.css"),
    
      // Get the JS
      $.getScript("/assets/feature.js")
    
    ).then(function(html, css, script) {
       // do stuff
    });
    

    Example: http://jsfiddle.net/C4vym/

  5. Permalink to comment#

    This also may be old hat to many programmers, but I found this article on how async approach differs from multi-threaded approach. The analogy of a burger joint where clerks serve up burgers to customers is neat, where the grill and oven represent resources. I wonder if it is truly accurate?

    http://alookonthecode.blogspot.com/2012/10/simply-explained-multithreaded-vs-async.html

  6. Thanks for this tutorial, it really comes handy. I’ve been recently working on a project which is all about loading templates and jsons with data to fill in. As I didn’t know this technique, it was all like: load template – wait – load json – wait – work with the data – show them. This really helps.

    In my project, I don’t know if the source I’m trying to get with ajax does exist or not, I can’t rely on “then” callback. It fails if it can’t download the source. If you’re wondering how to deal with this situation, this is the right way:

    $.when(
    
    // get template
    // get json
    
    ).done(function( template, json ) {
    
        // Everything OK
    
    }).fail(function() {
    
        // One of the sources is not available
    
    });
    
  7. Permalink to comment#

    If you like this type of thing (and I do like it very much), then this will blow your mind:

    You can also queue the loading of multiple images via Promises for example

    ` for (var i = 0; i < assets.length; i++) {
    var promise = imagePromise(assets[i]);
    promises.push(promise);
    }

     $.when.apply($, promises).done(function () {
     // do something with the images 
    });
    
    
      function imagePromise(src)  {
            var deferred = $.Deferred();
            var img = new Image();
    
        function resolve() {
        // Resolution callbacks receive the image, which you can then inject into the DOM
        //  to avoid triggering an extra HTTP request in IE
                return deferred.resolve(img);
            }
    
        // Resolution events
        $(img).on({
            error: deferred.reject,
            load: resolve
        });
    
        // Attach the source afterwards, since DOM synchronicity is weird:
        // A cached image will sometimes load or error on assignment
        img.src = src;
    
        return deferred.promise();
    

    }`

    • Owen Jones
      Permalink to comment#

      Bit beyond the needs of many, but that is pretty fucking awesome.

  8. Permalink to comment#

    Just search about jQuery “when” function yesterday and now I see a real tutorial about that. This is nice feature! I used request-in-request technique before and felt very bad with the speed. This is very useful if we develop an app that communicate between client and server.

    Thanks for sharing.

  9. Would this also work? All the AJAX callbacks call the same function, but that function won’t run until all three are back. It means the AJAX calls don’t need to be in the same place in the code and can be reused individually.

    var myApp = myApp || {};
    
    $.get('some.css', function(css) {
      myApp.css = css;
      doSomething();
    });
    $.get('some.js', function(js) {
      myApp.js = js;
      doSomething();
    });
    $.get('some.html', function(html) {
      myApp.html = html;
      doSomething();
    });
    
    function doSomething() {
      if (myApp.css && myApp.js && myApp.html) {
        //do something with css, js and html
      }
    }
    
    • That’s not incorrect, but it feels a little funky to me. Like intentionally avoiding structure. But it still would be faster than nested callbacks.

    • David, exactly! Your snipped works in same way. Please take a look at nekman’s comment above. I think, promises are much cleaner and has more abilities to extend.

    • David, your code works, but it’s unnecessary if you’re thinking reusability. If you’re going to reuse those AJAX requests in the future, put them in a function and return the promise object for each AJAX call. You’ll get all the benefits of promises plus can call the AJAX calls separately if you need it.

      function getCSS() {
        return $.get('some.css', function(css) { ... }).promise();
      }
      
      function getJS() {
        return $.get('some.js', function(js) { ... }).promise();
      }
      
      //Get Both
      $.when(getCSS(), getJS()).then(function(css, js) { ... });
      
      //Get One
      $.when(getCSS()).then(function(css) { ... });
      
    • Thanks for the feedback guys. Yeah Benjamin I like your way, less hacky than mine.

    • That is roughly what jQuery does internally. Whenever a promise is ready, it calls a function until all promises are done.

      Your code is not funky nor bad. It’s an equivalent in vanilla/native JavaScript which actually prevents a lot of unneccisary jQuery calls, so use whatever you prefer.

  10. Recently I’ve created the “Mobile First“ approach demo. My example shows how to bind on matchMedia and load assets on demand. Inner logic is very simple: use <link> and <script> tags to load assets for mobiles; bind on particular media-queries to load additional assets when specified event occurs. So, it works like a charm for me.

    I’m using well known yepnope.js to load CSS and JS there. It provides good layer of abstraction and saves from callback hell as well.

  11. Ramesh chowdarapally
    Permalink to comment#

    Really nice information. It reduces loading time and good user experience.

  12. I used to do all my DOM manipulations using callbacks like this in jQuery. But as the project gets bigger and bigger everything becomes so complex that it can drive you nuts. Now-a-days I use Reactjs to render different sections of my page, and from a designer perspective Reactjs is so cool unlike other complex solutions like Angularjs or Emberjs.

  13. Permalink to comment#

    Interesting though this part is quite tricky in my opinion:

    The problem? It’s slow.
    One request … wait to be done … another request … wait to be done … another request … wait to be done … go.

    I disagree here, it is more like:

    Send request #1, send request #2, send request #3.
    Handle the result #1, handle the result #2, handle the result #3.

    Since those function calls are asynchronous, the requests in the code initially written are already sent asynchronously.
    So in the end I think it’s quite the same.

  14. Permalink to comment#

    Thanks for this trick.. Will try this next time.

  15. T.J. Crowder
    Permalink to comment#

    Your initial code block has an extraneous statusCode layer. The correct version is:

    $.ajax({
      url: "/feature",
      success: function() {
        // Ajax success
      }
    });
    
  16. Permalink to comment#

    I used almost the same technique here: http://horiajurcut.com/the-perfect-request/

  17. Permalink to comment#

    That’s such a useful technique for conditional loading extra content fast. Thanks. :)

  18. Lum3ll
    Permalink to comment#

    Really useful… thanks! :)

  19. sam
    Permalink to comment#

    Nice snippet. Thanks.

    Looking at the comments, Can someone elaborate on promises please?

  20. Jesús
    Permalink to comment#

    Nothing new, but the explanation was straight to the point a and very easy to follow. Best intro to Deferreds.

  21. You can also do a callback for jQuery html()

      $('#divId').html(someText).promise().done(function(){
         //your callback logic / code here
      });
    
  22. What is the maximum ajax calls it can make at once?

  23. The server load and the number of client requests to the server should be the same. I hav not yet test this trick but initially I think there would be no differences between this methods and normal method.

  24. Mark Matias
    Permalink to comment#

    Good explenation and some very helpful tips. Thank you.

  25. Permalink to comment#

    Here’s the pattern I tend to use:

    var getCss = $.get('some.css', function(css) {
        // do something with it
    });
    var getJs = $.get('some.js', function(js) {
        // do something with it
    });
    
    $.when(getCss, getJs)
        .done(function(css, js) {
            //initialize app
        }).fail(function(){
            //notify user of error
        });
    

    I have a few posts out on how to use $.when with backbone work:
    Waiting for multiple collections to load &
    Application Bootstrapping

    • Permalink to comment#

      Scratch that. I meant to clean up the first couple of lines I copied from above:

      var getCss = $.get('some.css');
      var getJs = $.get('some.js');
      
      $.when(getCss, getJs)
          .done(function(css, js) {
              //initialize app
          }).fail(function(){
              //notify user of error
          });
      
  26. Permalink to comment#

    I would give Q and BlueBird a look when using promises they are the fastest and made the most sense to me. jQuery’s deferred method doesn’t entirely meet the promises spec and is one of the slowest next to vanilla JavaScript implementations. http://jsperf.com/promise-comparisons/63

  27. The code snippet after this text, “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.” has an issue. You have $.when( … }.then

  28. matchMedia. Thanks for slipping that in there. Looks like Irish and friends have a polyfill for it too.

  29. Victor
    Permalink to comment#

    Most time saving and clear explanation and examples. Many thanks!

  30. Thank you so much for such a nice explanation of using jQuery.when().

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".