…llbacks.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -59,9 +59,9 @@ var jQuery = function( selector, context ) { | ||
|
||
// Has the ready events already been bound? | ||
readyBound = false, | ||
|
||
// The functions to execute on DOM ready | ||
readyList = [], | ||
// The deferred used on DOM ready | ||
readyList, | ||
|
||
// The ready event handler | ||
DOMContentLoaded, | ||
@@ -75,7 +75,10 @@ var jQuery = function( selector, context ) { | ||
indexOf = Array.prototype.indexOf, | ||
|
||
// [[Class]] -> type pairs | ||
class2type = {}; | ||
class2type = {}, | ||
|
||
// Marker for deferred | ||
deferredMarker = []; | ||
|
||
jQuery.fn = jQuery.prototype = { | ||
init: function( selector, context ) { | ||
@@ -252,23 +255,13 @@ jQuery.fn = jQuery.prototype = { | ||
each: function( callback, args ) { | ||
return jQuery.each( this, callback, args ); | ||
}, | ||
|
||
ready: function( fn ) { | ||
ready: function() { | ||
// Attach the listeners | ||
jQuery.bindReady(); | ||
|
||
// If the DOM is already ready | ||
if ( jQuery.isReady ) { | ||
// Execute the function immediately | ||
fn.call( document, jQuery ); | ||
|
||
// Otherwise, remember the function for later | ||
} else if ( readyList ) { | ||
// Add the function to the wait list | ||
readyList.push( fn ); | ||
} | ||
|
||
return this; | ||
|
||
// Change ready & apply | ||
return ( jQuery.fn.ready = readyList.then ).apply( this , arguments ); | ||
}, | ||
|
||
eq: function( i ) { | ||
@@ -415,23 +408,11 @@ jQuery.extend({ | ||
} | ||
|
||
// If there are functions bound, to execute | ||
if ( readyList ) { | ||
// Execute all of them | ||
var fn, | ||
i = 0, | ||
ready = readyList; | ||
|
||
// Reset the list of functions | ||
readyList = null; | ||
|
||
while ( (fn = ready[ i++ ]) ) { | ||
fn.call( document, jQuery ); | ||
} | ||
|
||
// Trigger any bound ready events | ||
if ( jQuery.fn.trigger ) { | ||
jQuery( document ).trigger( "ready" ).unbind( "ready" ); | ||
} | ||
readyList.fire( document , [ jQuery ] ); | ||
|
||
// Trigger any bound ready events | ||
if ( jQuery.fn.trigger ) { | ||
jQuery( document ).trigger( "ready" ).unbind( "ready" ); | ||
} | ||
} | ||
}, | ||
@@ -800,6 +781,160 @@ jQuery.extend({ | ||
now: function() { | ||
return (new Date()).getTime(); | ||
}, | ||
|
||
// Create a simple deferred (one callbacks list) | ||
_deferred: function( cancellable ) { | ||
|
||
// cancellable by default | ||
cancellable = cancellable !== false; | ||
|
||
var // callbacks list | ||
callbacks = [], | ||
// stored [ context , args ] | ||
fired, | ||
// to avoid firing when already doing so | ||
firing, | ||
// flag to know if the deferred has been cancelled | ||
cancelled, | ||
// the deferred itself | ||
deferred = { | ||
|
||
// then( f1, f2, ...) | ||
then: function() { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jaubourg
Member
|
||
|
||
if ( ! cancelled ) { | ||
|
||
var args = arguments, | ||
i, | ||
type, | ||
_fired; | ||
|
||
if ( fired ) { | ||
_fired = fired; | ||
fired = 0; | ||
} | ||
|
||
for ( i in args ) { | ||
i = args[ i ]; | ||
type = jQuery.type( i ); | ||
if ( type === "array" ) { | ||
this.then.apply( this , i ); | ||
} else if ( type === "function" ) { | ||
callbacks.push( i ); | ||
} | ||
} | ||
|
||
if ( _fired ) { | ||
deferred.fire( _fired[ 0 ] , _fired[ 1 ] ); | ||
} | ||
} | ||
return this; | ||
}, | ||
|
||
// resolve with given context and args | ||
// (i is used internally) | ||
fire: function( context , args , i ) { | ||
if ( ! cancelled && ! fired && ! firing ) { | ||
firing = 1; | ||
try { | ||
for( i = 0 ; ! cancelled && callbacks[ i ] ; i++ ) { | ||
cancelled = ( callbacks[ i ].apply( context , args ) === false ) && cancellable; | ||
} | ||
} catch( e ) { | ||
cancelled = cancellable; | ||
jQuery.error( e ); | ||
} finally { | ||
fired = [ context , args ]; | ||
callbacks = cancelled ? [] : callbacks.slice( i + 1 ); | ||
firing = 0; | ||
} | ||
} | ||
return this; | ||
}, | ||
|
||
// resolve with this as context and given arguments | ||
resolve: function() { | ||
deferred.fire( this , arguments ); | ||
return this; | ||
}, | ||
|
||
// cancelling further callbacks | ||
cancel: function() { | ||
if ( cancellable ) { | ||
callbacks = []; | ||
cancelled = 1; | ||
} | ||
return this; | ||
} | ||
|
||
}; | ||
|
||
// Add the deferred marker | ||
deferred.then._ = deferredMarker; | ||
|
||
return deferred; | ||
}, | ||
|
||
// Full fledged deferred (two callbacks list) | ||
// Typical success/error system | ||
deferred: function( func , cancellable ) { | ||
|
||
// Handle varargs | ||
if ( arguments.length === 1 ) { | ||
|
||
if ( typeof func === "boolean" ) { | ||
cancellable = func; | ||
func = 0; | ||
} | ||
} | ||
|
||
var errorDeferred = jQuery._deferred( cancellable ), | ||
deferred = jQuery._deferred( cancellable ), | ||
// Keep reference of the cancel method since we'll redefine it | ||
cancelThen = deferred.cancel; | ||
|
||
// Add errorDeferred methods and redefine cancel | ||
jQuery.extend( deferred , { | ||
|
||
fail: errorDeferred.then, | ||
fireReject: errorDeferred.fire, | ||
reject: errorDeferred.resolve, | ||
cancel: function() { | ||
cancelThen(); | ||
errorDeferred.cancel(); | ||
return this; | ||
} | ||
|
||
} ); | ||
|
||
// Make sure only one callback list will be used | ||
deferred.then( errorDeferred.cancel ).fail( cancelThen ); | ||
|
||
// Call given func if any | ||
if ( func ) { | ||
func.call( deferred , deferred ); | ||
} | ||
|
||
return deferred; | ||
}, | ||
|
||
// Check if an object is a deferred | ||
isDeferred: function( object , method ) { | ||
method = method || "then"; | ||
return !!( object && object[ method ] && object[ method ]._ === deferredMarker ); | ||
}, | ||
|
||
// Deferred helper | ||
when: function( object , method ) { | ||
method = method || "then"; | ||
object = jQuery.isDeferred( object , method ) ? | ||
object : | ||
jQuery.deferred().resolve( object ); | ||
object.fail = object.fail || function() { return this; }; | ||
object[ method ] = object[ method ] || object.then; | ||
object.then = object.then || object[ method ]; | ||
return object; | ||
}, | ||
|
||
// Use of jQuery.browser is frowned upon. | ||
// More details: http://docs.jquery.com/Utilities/jQuery.browser | ||
@@ -818,6 +953,11 @@ jQuery.extend({ | ||
browser: {} | ||
}); | ||
|
||
// Create readyList deferred | ||
// also force $.fn.ready to be recognized as a defer | ||
readyList = jQuery._deferred( false ); | ||
jQuery.fn.ready._ = deferredMarker; | ||
|
||
// Populate the class2type map | ||
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { | ||
class2type[ "[object " + name + "]" ] = name.toLowerCase(); | ||
It's totally awesome to see Deferreds/promises added to jQuery with API basically compatible with other libraries, great addition! However, a slight nit: in all other libraries that implement thenable promises/deferreds (node-promise, promised-io, Dojo, ringo, etc.), the signature is usually then(callbackForSuccess, callbackForFail). How do you register a fail handler/callback with this API (to catch errors or when fail() is called)? I like your approach of allowing for adding multiple callbacks in a single call, and this would still be possible by allowing an array to be used as the first arg while reserving the second arg for the error callback (at least in the Deferred impl, vs the _Deferred impl).