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

Incredible Demos

  • By
    Introducing MooTools ElementSpy

    One part of MooTools I love is the ease of implementing events within classes. Just add Events to your Implements array and you can fire events anywhere you want -- these events are extremely helpful. ScrollSpy and many other popular MooTools plugins would...

  • By
    Fx.Rotate:  Animated Element Rotation with MooTools

    I was recently perusing the MooTools Forge and I saw a neat little plugin that allows for static element rotation: Fx.Rotate. Fx.Rotate is an extension of MooTools' native Fx class and rotates the element via CSS within each A-grade browser it...

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!