Promises in jQuery

There's a good but pretty long post about Promises in JavaScript that's worth skimming over. The author had a hand in defining the Promises/A+ spec, a successor to the original Promises/A spec.

jQuery has had its own idea of promises for a while. There's jQuery.promise(), which operates on a CSS selector and is used for determining when animations have completed on DOM elements.

There's also jQuery.Deferred, which is a type of object based on the Promises/A spec, but, as pointed out by the blog post I linked to, badly broken in how it handles exceptions. I want to counter that argument by saying that jQuery's promise implementation has been working fine for me. Maybe it doesn't strictly conform to any documented specification, but I haven't found it necessary to introduce a better promise library into Mediathread.

I've found promises to be a useful abstraction for working with XHR requests. Fortunately, in jQuery, a call to $.ajax() or its related functions returns a jqXHR object, which happens to behave like a Promise:

The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise.

You can use promises to perform operations that would otherwise be really complicated. For example, consider this function that makes an xhr request to load a Mustache template:

    /**
     * Load a Mustache template from the /media/templates/
     * directory and put it in the MediaThread.templates
     * dictionary.
     *
     * Returns a jqXHR object.
     */
    MediaThread.loadTemplate = function(templateName) {
	return jQuery.ajax({
	    url: '/media/templates/' + templateName + '.mustache',
	    dataType: 'text',
	    success: function(text) {
		MediaThread.templates[templateName] = text;
	    }
	});
    };
  

You can then define a function that takes a bunch of template names, and returns an array of jqXHR objects.

    /**
     * Load all the asset-related templates.
     *
     * Returns an array of jqXHR objects.
     */
    var loadTemplates = function(templateNames) {
	var promises = [];

	for (var i = 0; i < templateNames.length; i++) {
	    promises.push(MediaThread.loadTemplate(templateNames[i]));
	}

	return promises;
    };
  

Given this array of "thenable" jqXHR promises, how would you determine when everything has been downloaded? More "modern" libraries like RSVP.js have nice methods for dealing with arrays of promises. It's also possible to do this with jQuery, using jQuery.when(). Notice how jQuery.when() doesn't take an array, but expects the arguments to come in as separate comma-separated parameters. I remembered a way to pass the arguments to a JavaScript function as an array, using apply(), so you can do something like this:

    var self = this;
    jQuery.when.apply(this, loadTemplates('a', 'b', 'c', 'd'))
      .then(function() {
        // This is triggered after each promise created by
        // loadTemplates() has either been resolved or
        // rejected.
        self.initAfterTemplatesLoad();
      });