Moves Deferred-related code into a separate module. Context handling has been simplif...
[jquery.git] / src / deferred.js
diff --git a/src/deferred.js b/src/deferred.js
new file mode 100644 (file)
index 0000000..f0d7c08
--- /dev/null
@@ -0,0 +1,169 @@
+(function( jQuery ) {
+
+var // Promise methods
+       promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
+       // Static reference to slice
+       sliceDeferred = [].slice;
+
+jQuery.extend({
+       // Create a simple deferred (one callbacks list)
+       _Deferred: function() {
+               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  = {
+
+                               // done( f1, f2, ...)
+                               done: function() {
+                                       if ( !cancelled ) {
+                                               var args = arguments,
+                                                       i,
+                                                       length,
+                                                       elem,
+                                                       type,
+                                                       _fired;
+                                               if ( fired ) {
+                                                       _fired = fired;
+                                                       fired = 0;
+                                               }
+                                               for ( i = 0, length = args.length; i < length; i++ ) {
+                                                       elem = args[ i ];
+                                                       type = jQuery.type( elem );
+                                                       if ( type === "array" ) {
+                                                               deferred.done.apply( deferred, elem );
+                                                       } else if ( type === "function" ) {
+                                                               callbacks.push( elem );
+                                                       }
+                                               }
+                                               if ( _fired ) {
+                                                       deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+                                               }
+                                       }
+                                       return this;
+                               },
+
+                               // resolve with given context and args
+                               resolveWith: function( context, args ) {
+                                       if ( !cancelled && !fired && !firing ) {
+                                               // make sure args are available (#8421)
+                                               args = args || [];
+                                               firing = 1;
+                                               try {
+                                                       while( callbacks[ 0 ] ) {
+                                                               callbacks.shift().apply( context, args );
+                                                       }
+                                               }
+                                               finally {
+                                                       fired = [ context, args ];
+                                                       firing = 0;
+                                               }
+                                       }
+                                       return this;
+                               },
+
+                               // resolve with this as context and given arguments
+                               resolve: function() {
+                                       deferred.resolveWith( this, arguments );
+                                       return this;
+                               },
+
+                               // Has this deferred been resolved?
+                               isResolved: function() {
+                                       return !!( firing || fired );
+                               },
+
+                               // Cancel
+                               cancel: function() {
+                                       cancelled = 1;
+                                       callbacks = [];
+                                       return this;
+                               }
+                       };
+
+               return deferred;
+       },
+
+       // Full fledged deferred (two callbacks list)
+       Deferred: function( func ) {
+               var deferred = jQuery._Deferred(),
+                       failDeferred = jQuery._Deferred(),
+                       promise;
+               // Add errorDeferred methods, then and promise
+               jQuery.extend( deferred, {
+                       then: function( doneCallbacks, failCallbacks ) {
+                               deferred.done( doneCallbacks ).fail( failCallbacks );
+                               return this;
+                       },
+                       fail: failDeferred.done,
+                       rejectWith: failDeferred.resolveWith,
+                       reject: failDeferred.resolve,
+                       isRejected: failDeferred.isResolved,
+                       // Get a promise for this deferred
+                       // If obj is provided, the promise aspect is added to the object
+                       promise: function( obj ) {
+                               if ( obj == null ) {
+                                       if ( promise ) {
+                                               return promise;
+                                       }
+                                       promise = obj = {};
+                               }
+                               var i = promiseMethods.length;
+                               while( i-- ) {
+                                       obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
+                               }
+                               return obj;
+                       }
+               } );
+               // Make sure only one callback list will be used
+               deferred.done( failDeferred.cancel ).fail( deferred.cancel );
+               // Unexpose cancel
+               delete deferred.cancel;
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred, deferred );
+               }
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( firstParam ) {
+               var args = arguments,
+                       i = 0,
+                       length = args.length,
+                       count = length,
+                       deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+                               firstParam :
+                               jQuery.Deferred();
+               function resolveFunc( i ) {
+                       return function( value ) {
+                               args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+                               if ( !( --count ) ) {
+                                       deferred.resolveWith( deferred, args );
+                               }
+                       };
+               }
+               if ( length > 1 ) {
+                       for( ; i < length; i++ ) {
+                               if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
+                                       args[ i ].promise().then( resolveFunc(i), deferred.reject );
+                               } else {
+                                       --count;
+                               }
+                       }
+                       if ( !count ) {
+                               deferred.resolveWith( deferred, args );
+                       }
+               } else if ( deferred !== firstParam ) {
+                       deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+               }
+               return deferred.promise();
+       }
+});
+
+})( jQuery );