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
    Creating Scrolling Parallax Effects with CSS

    Introduction For quite a long time now websites with the so called "parallax" effect have been really popular. In case you have not heard of this effect, it basically includes different layers of images that are moving in different directions or with different speed. This leads to a...

  • By
    CSS Animations Between Media Queries

    CSS animations are right up there with sliced bread. CSS animations are efficient because they can be hardware accelerated, they require no JavaScript overhead, and they are composed of very little CSS code. Quite often we add CSS transforms to elements via CSS during...

Incredible Demos

  • By
    jQuery Comment Preview

    I released a MooTools comment preview script yesterday and got numerous requests for a jQuery version. Ask and you shall receive! I'll use the exact same CSS and HTML as yesterday. The XHTML The CSS The jQuery JavaScript On the keypress and blur events, we validate and...

  • By
    Create Your Own Dijit CSS Theme with LESS CSS

    The Dojo Toolkit seems to just get better and better.  One of the new additions in Dojo 1.6 was the use of LESS CSS to create Dijit themes.  The move to using LESS is a brilliant one because it makes creating your own Dijit theme...

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!