Landing pull request 511. Adding a little Makefile jQuery sizing utility to easily...
[jquery.git] / src / deferred.js
blobe543f151825933388d51826f1a1b0bcbfab35057
1 (function( jQuery ) {
3 var // Promise methods
4         promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
5         // Static reference to slice
6         sliceDeferred = [].slice;
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  = {
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                                 },
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                                 },
70                                 // resolve with this as context and given arguments
71                                 resolve: function() {
72                                         deferred.resolveWith( this, arguments );
73                                         return this;
74                                 },
76                                 // Has this deferred been resolved?
77                                 isResolved: function() {
78                                         return !!( firing || fired );
79                                 },
81                                 // Cancel
82                                 cancel: function() {
83                                         cancelled = 1;
84                                         callbacks = [];
85                                         return this;
86                                 }
87                         };
89                 return deferred;
90         },
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                         always: function() {
104                                 return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
105                         },
106                         fail: failDeferred.done,
107                         rejectWith: failDeferred.resolveWith,
108                         reject: failDeferred.resolve,
109                         isRejected: failDeferred.isResolved,
110                         pipe: function( fnDone, fnFail ) {
111                                 return jQuery.Deferred(function( newDefer ) {
112                                         jQuery.each( {
113                                                 done: [ fnDone, "resolve" ],
114                                                 fail: [ fnFail, "reject" ]
115                                         }, function( handler, data ) {
116                                                 var fn = data[ 0 ],
117                                                         action = data[ 1 ],
118                                                         returned;
119                                                 if ( jQuery.isFunction( fn ) ) {
120                                                         deferred[ handler ](function() {
121                                                                 returned = fn.apply( this, arguments );
122                                                                 if ( returned && jQuery.isFunction( returned.promise ) ) {
123                                                                         returned.promise().then( newDefer.resolve, newDefer.reject );
124                                                                 } else {
125                                                                         newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
126                                                                 }
127                                                         });
128                                                 } else {
129                                                         deferred[ handler ]( newDefer[ action ] );
130                                                 }
131                                         });
132                                 }).promise();
133                         },
134                         // Get a promise for this deferred
135                         // If obj is provided, the promise aspect is added to the object
136                         promise: function( obj ) {
137                                 if ( obj == null ) {
138                                         if ( promise ) {
139                                                 return promise;
140                                         }
141                                         promise = obj = {};
142                                 }
143                                 var i = promiseMethods.length;
144                                 while( i-- ) {
145                                         obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
146                                 }
147                                 return obj;
148                         }
149                 });
150                 // Make sure only one callback list will be used
151                 deferred.done( failDeferred.cancel ).fail( deferred.cancel );
152                 // Unexpose cancel
153                 delete deferred.cancel;
154                 // Call given func if any
155                 if ( func ) {
156                         func.call( deferred, deferred );
157                 }
158                 return deferred;
159         },
161         // Deferred helper
162         when: function( firstParam ) {
163                 var args = arguments,
164                         i = 0,
165                         length = args.length,
166                         count = length,
167                         deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
168                                 firstParam :
169                                 jQuery.Deferred();
170                 function resolveFunc( i ) {
171                         return function( value ) {
172                                 args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
173                                 if ( !( --count ) ) {
174                                         // Strange bug in FF4:
175                                         // Values changed onto the arguments object sometimes end up as undefined values
176                                         // outside the $.when method. Cloning the object into a fresh array solves the issue
177                                         deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
178                                 }
179                         };
180                 }
181                 if ( length > 1 ) {
182                         for( ; i < length; i++ ) {
183                                 if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
184                                         args[ i ].promise().then( resolveFunc(i), deferred.reject );
185                                 } else {
186                                         --count;
187                                 }
188                         }
189                         if ( !count ) {
190                                 deferred.resolveWith( deferred, args );
191                         }
192                 } else if ( deferred !== firstParam ) {
193                         deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
194                 }
195                 return deferred.promise();
196         }
199 })( jQuery );