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
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

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
    Animated AJAX Record Deletion Using Dojo

    I'm a huge fan of WordPress' method of individual article deletion. You click the delete link, the menu item animates red, and the item disappears. Here's how to achieve that functionality with Dojo JavaScript. The PHP - Content & Header The following snippet goes at the...

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!