How to use jQuery Deferred with custom events?

Adam Flynn picture Adam Flynn · Feb 15, 2011 · Viewed 11.2k times · Source

I have two abstracted processes (e.g. managed within js objects using the revealing module pattern that do not expose their internals) that fire custom events when they complete. I want to perform an action when both custom events have fired.

The new Deferred logic in jQuery 1.5 seems like it would be an ideal way to manage this, except that the when() method takes Deferred objects that return a promise() (or normal js objects, but then when() completes immediately instead of waiting, which is useless to me).

Ideally I would like to do something like:

//execute when both customevent1 and customevent2 have been fired
$.when('customevent1 customevent2').done(function(){
  //do something
});

What would be the best way to marry these two techniques?

Answer

Julian Aubourg picture Julian Aubourg · Feb 16, 2011

http://jsfiddle.net/ch47n/

I created a small plugin that creates a new jQuery.fn.when method.

Syntax is:

jQuery( "whatever" ).when( "event1 event2..." ).done( callback );

It uses jQuery.when() extensively internally and ensures that all events have been triggered on all elements in the collection before resolving.


Actual plugin code below:

( function( $ ) {

    $.fn.when = function( events ) {

        var deferred, $element, elemIndex, eventIndex;

        // Get the list of events
        events = events.split( /\s+/g );

        // We will store one deferred per event and per element
        var deferreds = [];

        // For each element
        for( elemIndex = 0; elemIndex < this.length; elemIndex++ ) {
            $element = $( this[ elemIndex ] );
            // For each event
            for ( eventIndex = 0; eventIndex < events.length; eventIndex++ ) {
                // Store a Deferred...
                deferreds.push(( deferred = $.Deferred() ));
                // ... that is resolved when the event is fired on this element
                $element.one( events[ eventIndex ], deferred.resolve );
            }
        }

        // Return a promise resolved once all events fired on all elements
        return $.when.apply( null, deferreds );
    };

} )( jQuery );