Moves Deferred-related code into a separate module. Context handling has been simplif...
[jquery.git] / src / deferred.js
1 (function( jQuery ) {
2
3 var // Promise methods
4         promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
5         // Static reference to slice
6         sliceDeferred = [].slice;
7
8 jQuery.extend({
9         // Create a simple deferred (one callbacks list)
10         _Deferred: function() {
11                 var // callbacks list
12                         callbacks = [],
13                         // stored [ context , args ]
14                         fired,
15                         // to avoid firing when already doing so
16                         firing,
17                         // flag to know if the deferred has been cancelled
18                         cancelled,
19                         // the deferred itself
20                         deferred  = {
21
22                                 // done( f1, f2, ...)
23                                 done: function() {
24                                         if ( !cancelled ) {
25                                                 var args = arguments,
26                                                         i,
27                                                         length,
28                                                         elem,
29                                                         type,
30                                                         _fired;
31                                                 if ( fired ) {
32                                                         _fired = fired;
33                                                         fired = 0;
34                                                 }
35                                                 for ( i = 0, length = args.length; i < length; i++ ) {
36                                                         elem = args[ i ];
37                                                         type = jQuery.type( elem );
38                                                         if ( type === "array" ) {
39                                                                 deferred.done.apply( deferred, elem );
40                                                         } else if ( type === "function" ) {
41                                                                 callbacks.push( elem );
42                                                         }
43                                                 }
44                                                 if ( _fired ) {
45                                                         deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
46                                                 }
47                                         }
48                                         return this;
49                                 },
50
51                                 // resolve with given context and args
52                                 resolveWith: function( context, args ) {
53                                         if ( !cancelled && !fired && !firing ) {
54                                                 // make sure args are available (#8421)
55                                                 args = args || [];
56                                                 firing = 1;
57                                                 try {
58                                                         while( callbacks[ 0 ] ) {
59                                                                 callbacks.shift().apply( context, args );
60                                                         }
61                                                 }
62                                                 finally {
63                                                         fired = [ context, args ];
64                                                         firing = 0;
65                                                 }
66                                         }
67                                         return this;
68                                 },
69
70                                 // resolve with this as context and given arguments
71                                 resolve: function() {
72                                         deferred.resolveWith( this, arguments );
73                                         return this;
74                                 },
75
76                                 // Has this deferred been resolved?
77                                 isResolved: function() {
78                                         return !!( firing || fired );
79                                 },
80
81                                 // Cancel
82                                 cancel: function() {
83                                         cancelled = 1;
84                                         callbacks = [];
85                                         return this;
86                                 }
87                         };
88
89                 return deferred;
90         },
91
92         // Full fledged deferred (two callbacks list)
93         Deferred: function( func ) {
94                 var deferred = jQuery._Deferred(),
95                         failDeferred = jQuery._Deferred(),
96                         promise;
97                 // Add errorDeferred methods, then and promise
98                 jQuery.extend( deferred, {
99                         then: function( doneCallbacks, failCallbacks ) {
100                                 deferred.done( doneCallbacks ).fail( failCallbacks );
101                                 return this;
102                         },
103                         fail: failDeferred.done,
104                         rejectWith: failDeferred.resolveWith,
105                         reject: failDeferred.resolve,
106                         isRejected: failDeferred.isResolved,
107                         // Get a promise for this deferred
108                         // If obj is provided, the promise aspect is added to the object
109                         promise: function( obj ) {
110                                 if ( obj == null ) {
111                                         if ( promise ) {
112                                                 return promise;
113                                         }
114                                         promise = obj = {};
115                                 }
116                                 var i = promiseMethods.length;
117                                 while( i-- ) {
118                                         obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
119                                 }
120                                 return obj;
121                         }
122                 } );
123                 // Make sure only one callback list will be used
124                 deferred.done( failDeferred.cancel ).fail( deferred.cancel );
125                 // Unexpose cancel
126                 delete deferred.cancel;
127                 // Call given func if any
128                 if ( func ) {
129                         func.call( deferred, deferred );
130                 }
131                 return deferred;
132         },
133
134         // Deferred helper
135         when: function( firstParam ) {
136                 var args = arguments,
137                         i = 0,
138                         length = args.length,
139                         count = length,
140                         deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
141                                 firstParam :
142                                 jQuery.Deferred();
143                 function resolveFunc( i ) {
144                         return function( value ) {
145                                 args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
146                                 if ( !( --count ) ) {
147                                         deferred.resolveWith( deferred, args );
148                                 }
149                         };
150                 }
151                 if ( length > 1 ) {
152                         for( ; i < length; i++ ) {
153                                 if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
154                                         args[ i ].promise().then( resolveFunc(i), deferred.reject );
155                                 } else {
156                                         --count;
157                                 }
158                         }
159                         if ( !count ) {
160                                 deferred.resolveWith( deferred, args );
161                         }
162                 } else if ( deferred !== firstParam ) {
163                         deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
164                 }
165                 return deferred.promise();
166         }
167 });
168
169 })( jQuery );