jQuery: Multiple AJAX and JSON Requests, One Callback

By  on  

I've been working on a new feature for the Mozilla Developer Network which requires loading of a basic script file as well as a JSON stream.  Since we use jQuery, that means a jQuery.getScript and a jQuery.getJSON.  I know those both work asynchronously and return a Deferred, so I wondered if there was a way that I could load them in parallel with one callback, much the way JavaScript loaders like curljs do.  I was in luck!  Using jQuery.when, I can load both requests concurrently with one callback!

The jQuery JavaScript

As I mentioned, my use case was loading a script and a JSON resource, so how it works is:

$.when(
	$.getScript('/media/js/wiki-min.js?build=21eb633'), 
	$.getJSON('https://developer.mozilla.org/en-US/demos/feeds/json/featured/')
).then(function(a, b) {  // or ".done"
	
	// Yay, stuff loaded and now we can do something!

});

When the resources are done loading, the done or then callback fires and I know the requests are complete.  Each request type provides a different callback argument object, so the above would provide:

// [response, state, jqxhr], [response, state, jqxhr]
["(function(c){var e=c(".from-search-navigate");if(e…;if(j){g.apply(m,l)}}}})(window,document,jQuery);", "success", Object]
[Array[15], "success", Object]

If we wanted to add a traditional AJAX XHR request to the mix, say a widget template, we could do that too:

$.when(
	$.getScript('/media/js/wiki-min.js?build=21eb633'), 
	$.getJSON('https://developer.mozilla.org/en-US/demos/feeds/json/featured/'), 
	$.get('/')
).then(function(a, b, c) { 
	console.log(a, b, c); 
});

The Dojo Toolkit has had this type of functionality for a long time but I'm stoked that modern jQuery allows for the same.  Making multiple requests with one callback seems as relevant as any other task these days, so jQuery's definitely moving with the times!

Recent Features

  • By
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

  • By
    I’m an Impostor

    This is the hardest thing I've ever had to write, much less admit to myself.  I've written resignation letters from jobs I've loved, I've ended relationships, I've failed at a host of tasks, and let myself down in my life.  All of those feelings were very...

Incredible Demos

  • By
    Multiple Backgrounds with CSS

    Anyone that's been in the web development industry for 5+ years knows that there are certain features that we should have had several years ago. One of those features is the HTML5 placeholder; we used JavaScript shims for a decade before placeholder came...

  • By
    Fullscreen API

    As we move toward more true web applications, our JavaScript APIs are doing their best to keep up.  One very simple but useful new JavaScript API is the Fullscreen API.  The Fullscreen API provides a programmatic way to request fullscreen display from the user, and exit...

Discussion

  1. Drew Tipson

    You can also use .then to normalize or even combine the responses, parsing the arguments (rather than explicitly assigning them in .then(function) as this gets a little tricky with .when).

    Since when accepts an array of Deffereds, you can set it up so that it can handle an array of any length, combining the results all into one response before success/fail handlers are fired. That way your success/fail handlers don’t have to know anything about the complexity or even the number of the original set of requests. This is great for batching.

  2. You can do some nice tricks with multiple promises http://stackoverflow.com/a/6162959/373007

    I use this in ownCloud contacts app to know when all address books are loaded.

  3. They’ve had this functionality since version 1.5, which came out over 3 years ago. Don’t make it sound like it’s new.

    • New to me?

    • Dave

      Probably like many devs, it’s completely new to me too and I’ve used jquery for ages, so thank-you for sharing! Some people like to show they know more than others, best to just ignore’em :)

  4. Yea, Joe is right, it was it came about three years ago. But I don’t think David tries to show that it is some kind of latest discovery or something! Well, let it go, overall it is a good practice but most of the devs are unaware of it, so thanks for such a nice sharing :)

  5. Oh, very nice, thanks for sharing this with us!

  6. Nice find – just what I was looking for!

  7. It is awesome trick, Still working with my new project. Thanks david.

  8. Drew

    Another useful pattern: $.when.apply($, arrayOfDeferreds)

    That allows you to create the batch of Deferreds elsewhere, and have it be of any length (so that the batching call doesn’t have to know or care), as long as your .then handler knows how to recombine the resulting requests.

  9. I’ve been using this for a while now, but good to know. Another useful trick I use with this and $.Deferreds in general to manage what value is returned when the deferred is resolved. One way I do this is to wrap this in a function like so:

    function handler() {
      var dfd = $.Deferred(),
            dfds = [];
      // get your array of deferreds however
      $.when.appy($, dfds).then(function() {
        var retVal = 'whatever I want';
        dfd.resolve(retVal);
      }, function() {
        dfd.reject(arguments);
      });
      return dfd;
    }
    

    Then you can use the handler function just like a regular deferred and you’re also encapsulating the arguments being passed when the deferred resolves. You could achieve similar encapsulation by using deferred.pipe. Then you don’t have to manage another deferred.

    • You should return the promise instead of the deferred object, otherwise the caller could easily change the deferred object (what you don’t want…).

      return dfd.promise();
  10. gkatz

    when doing:

    $.when($.getScript('/a.js'), $.getJSON('b.json')
    ).then
    

    is the order promised? will a.js always be loaded before b.json?

  11. gkatz

    so a year and a half after you wrote this blog I still found it and used it…

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!