]> source.dussan.org Git - jquery.git/commitdiff
Trims down deferred.js. The gist of it is from the magnificent @gibson042, I just...
authorjaubourg <j@ubourg.net>
Wed, 25 Apr 2012 16:24:34 +0000 (18:24 +0200)
committerjaubourg <j@ubourg.net>
Wed, 25 Apr 2012 16:25:52 +0000 (18:25 +0200)
src/deferred.js

index 0cb9e06a55eafecc2be2cc2b4b8458e5d66c32a7..e39502783c4eb36c9922eabdfb430d5896ebae1d 100644 (file)
@@ -6,90 +6,82 @@ var // Static reference to slice
 jQuery.extend({
 
        Deferred: function( func ) {
-               var doneList = jQuery.Callbacks( "once memory" ),
-                       failList = jQuery.Callbacks( "once memory" ),
-                       progressList = jQuery.Callbacks( "memory" ),
+               var tuples = [
+                               // action, add listener, listener list
+                               [ "resolve", "done", jQuery.Callbacks("once memory") ],
+                               [ "reject", "fail", jQuery.Callbacks("once memory") ],
+                               [ "notify", "progress", jQuery.Callbacks("memory") ]
+                       ],
                        state = "pending",
-                       lists = {
-                               resolve: doneList,
-                               reject: failList,
-                               notify: progressList
-                       },
                        promise = {
-                               done: doneList.add,
-                               fail: failList.add,
-                               progress: progressList.add,
-
                                state: function() {
                                        return state;
                                },
-
-                               // Deprecated
-                               isResolved: doneList.fired,
-                               isRejected: failList.fired,
-
                                always: function() {
-                                       deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+                                       deferred.done( arguments ).fail( arguments );
                                        return this;
                                },
-                               then: function( fnDone, fnFail, fnProgress ) {
+                               then: function( /* fnDone, fnFail, fnProgress */ ) {
+                                       var fns = arguments;
                                        return jQuery.Deferred(function( newDefer ) {
-                                               jQuery.each( {
-                                                       done: [ fnDone, "resolve" ],
-                                                       fail: [ fnFail, "reject" ],
-                                                       progress: [ fnProgress, "notify" ]
-                                               }, function( handler, data ) {
-                                                       var fn = data[ 0 ],
-                                                               action = data[ 1 ],
-                                                               returned;
-                                                       if ( jQuery.isFunction( fn ) ) {
-                                                               deferred[ handler ](function() {
-                                                                       returned = fn.apply( this, arguments );
+                                               jQuery.each( tuples, function( i, tuple ) {
+                                                       var action = tuple[ 0 ],
+                                                               fn = fns[ i ];
+                                                       // deferred[ done | fail | progress ] for forwarding actions to newDefer
+                                                       deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
+                                                               function() {
+                                                                       var returned = fn.apply( this, arguments );
                                                                        if ( returned && jQuery.isFunction( returned.promise ) ) {
-                                                                               returned.promise().done( newDefer.resolve ).fail( newDefer.reject ).progress( newDefer.notify );
+                                                                               returned.promise()
+                                                                                       .done( newDefer.resolve )
+                                                                                       .fail( newDefer.reject )
+                                                                                       .progress( newDefer.notify );
                                                                        } else {
                                                                                newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
                                                                        }
-                                                               });
-                                                       } else {
-                                                               deferred[ handler ]( newDefer[ action ] );
-                                                       }
+                                                               } :
+                                                               newDefer[ action ]
+                                                       );
                                                });
+                                               fns = null;
                                        }).promise();
                                },
                                // Get a promise for this deferred
                                // If obj is provided, the promise aspect is added to the object
                                promise: function( obj ) {
-                                       if ( obj == null ) {
-                                               obj = promise;
-                                       } else {
-                                               for ( var key in promise ) {
-                                                       obj[ key ] = promise[ key ];
-                                               }
-                                       }
-                                       return obj;
+                                       return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise;
                                }
                        },
-                       deferred,
-                       key;
+                       deferred = {};
 
                // Keep pipe for back-compat
                promise.pipe = promise.then;
 
-               // Construct deferred
-               deferred = promise.promise({});
+               // Add list-specific methods
+               jQuery.each( tuples, function( i, tuple ) {
+                       var list = tuple[ 2 ], stateString;
 
-               for ( key in lists ) {
-                       deferred[ key ] = lists[ key ].fire;
-                       deferred[ key + "With" ] = lists[ key ].fireWith;
-               }
+                       // promise[ done | fail | progress ] = list.add
+                       promise[ tuple[1] ] = list.add;
 
-               // Handle state
-               deferred.done( function() {
-                       state = "resolved";
-               }, failList.disable, progressList.lock ).fail( function() {
-                       state = "rejected";
-               }, doneList.disable, progressList.lock );
+                       // Handle state
+                       if ( i < 2 ) {
+                               stateString = tuple[ 0 ].replace( /e?$/, "ed" );
+                               list.add(function() {
+                                       state = stateString;
+                                       tuples[ i ^ 1 ][ 2 ].disable();
+                                       tuples[ 2 ][ 2 ].lock();
+                               });
+                               promise[ "isR" + stateString.substr( 1 ) ] = list.fired;
+                       }
+
+                       // deferred[ resolve | reject | notify ] = list.fire
+                       deferred[ tuple[0] ] = list.fire;
+                       deferred[ tuple[0] + "With" ] = list.fireWith;
+               });
+
+               // Make the deferred a promise
+               promise.promise( deferred );
 
                // Call given func if any
                if ( func ) {
@@ -101,45 +93,50 @@ jQuery.extend({
        },
 
        // Deferred helper
-       when: function( firstParam ) {
-               var args = sliceDeferred.call( arguments ),
-                       i = 0,
-                       length = args.length,
-                       pValues = new Array( length ),
-                       count = length,
-                       pCount = length,
-                       deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
-                               firstParam :
-                               jQuery.Deferred(),
-                       promise = deferred.promise();
-               function resolveFunc( i ) {
-                       return function( value ) {
-                               args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value;
-                               if ( !( --count ) ) {
-                                       deferred.resolveWith( deferred, args );
-                               }
-                       };
-               }
-               function progressFunc( i ) {
-                       return function( value ) {
-                               pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value;
-                               deferred.notifyWith( promise, pValues );
+       when: function( subordinate /* , ..., subordinateN */ ) {
+               var i = 0,
+                       resolveValues = sliceDeferred.call( arguments ),
+                       length = resolveValues.length,
+                       progressValues = new Array( length ),
+
+                       // the count of uncompleted subordinates
+                       remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+                       // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+                       deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+                       promise = deferred.promise(),
+
+                       // Update function for both resolve and progress values
+                       updateFunc = function( i, arr ) {
+                               return function( value ) {
+                                       arr[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value;
+                                       if( arr === progressValues ) {
+                                               deferred.notifyWith( promise, arr );
+                                       } else if ( !( --remaining ) ) {
+                                               deferred.resolveWith( promise, arr );
+                                       }
+                               };
                        };
-               }
+
+               // add listeners to Deferred subordinates; treat others as resolved
                if ( length > 1 ) {
                        for ( ; i < length; i++ ) {
-                               if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
-                                       args[ i ].promise().done( resolveFunc(i) ).fail( deferred.reject ).progress( progressFunc(i) );
+                               if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+                                       resolveValues[ i ].promise()
+                                               .done( updateFunc( i, resolveValues ) )
+                                               .fail( deferred.reject )
+                                               .progress( updateFunc( i, progressValues ) );
                                } else {
-                                       --count;
+                                       --remaining;
                                }
                        }
-                       if ( !count ) {
-                               deferred.resolveWith( deferred, args );
-                       }
-               } else if ( deferred !== firstParam ) {
-                       deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
                }
+
+               // if we're not waiting on anything, resolve the master
+               if ( !remaining ) {
+                       deferred.resolveWith( deferred, resolveValues );
+               }
+
                return promise;
        }
 });