From 6e066a4db72ff6b0d12dd8a43faec0a80e4a1fed Mon Sep 17 00:00:00 2001 From: jaubourg Date: Wed, 25 Apr 2012 18:24:34 +0200 Subject: Trims down deferred.js. The gist of it is from the magnificent @gibson042, I just added some minor touches. --- src/deferred.js | 175 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 86 insertions(+), 89 deletions(-) (limited to 'src/deferred.js') diff --git a/src/deferred.js b/src/deferred.js index 0cb9e06a5..e39502783 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -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; } }); -- cgit v1.2.3