source: Dev/trunk/src/client/dojo/Deferred.js @ 525

Last change on this file since 525 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 9.6 KB
Line 
1define([
2        "./has",
3        "./_base/lang",
4        "./errors/CancelError",
5        "./promise/Promise",
6        "./has!config-deferredInstrumentation?./promise/instrumentation"
7], function(has, lang, CancelError, Promise, instrumentation){
8        "use strict";
9
10        // module:
11        //              dojo/Deferred
12
13        var PROGRESS = 0,
14                        RESOLVED = 1,
15                        REJECTED = 2;
16        var FULFILLED_ERROR_MESSAGE = "This deferred has already been fulfilled.";
17
18        var freezeObject = Object.freeze || function(){};
19
20        var signalWaiting = function(waiting, type, result, rejection, deferred){
21                if(has("config-deferredInstrumentation")){
22                        if(type === REJECTED && Deferred.instrumentRejected && waiting.length === 0){
23                                Deferred.instrumentRejected(result, false, rejection, deferred);
24                        }
25                }
26
27                for(var i = 0; i < waiting.length; i++){
28                        signalListener(waiting[i], type, result, rejection);
29                }
30        };
31
32        var signalListener = function(listener, type, result, rejection){
33                var func = listener[type];
34                var deferred = listener.deferred;
35                if(func){
36                        try{
37                                var newResult = func(result);
38                                if(type === PROGRESS){
39                                        if(typeof newResult !== "undefined"){
40                                                signalDeferred(deferred, type, newResult);
41                                        }
42                                }else{
43                                        if(newResult && typeof newResult.then === "function"){
44                                                listener.cancel = newResult.cancel;
45                                                newResult.then(
46                                                                // Only make resolvers if they're actually going to be used
47                                                                makeDeferredSignaler(deferred, RESOLVED),
48                                                                makeDeferredSignaler(deferred, REJECTED),
49                                                                makeDeferredSignaler(deferred, PROGRESS));
50                                                return;
51                                        }
52                                        signalDeferred(deferred, RESOLVED, newResult);
53                                }
54                        }catch(error){
55                                signalDeferred(deferred, REJECTED, error);
56                        }
57                }else{
58                        signalDeferred(deferred, type, result);
59                }
60
61                if(has("config-deferredInstrumentation")){
62                        if(type === REJECTED && Deferred.instrumentRejected){
63                                Deferred.instrumentRejected(result, !!func, rejection, deferred.promise);
64                        }
65                }
66        };
67
68        var makeDeferredSignaler = function(deferred, type){
69                return function(value){
70                        signalDeferred(deferred, type, value);
71                };
72        };
73
74        var signalDeferred = function(deferred, type, result){
75                if(!deferred.isCanceled()){
76                        switch(type){
77                                case PROGRESS:
78                                        deferred.progress(result);
79                                        break;
80                                case RESOLVED:
81                                        deferred.resolve(result);
82                                        break;
83                                case REJECTED:
84                                        deferred.reject(result);
85                                        break;
86                        }
87                }
88        };
89
90        var Deferred = function(canceler){
91                // summary:
92                //              Creates a new deferred. This API is preferred over
93                //              `dojo/_base/Deferred`.
94                // description:
95                //              Creates a new deferred, as an abstraction over (primarily)
96                //              asynchronous operations. The deferred is the private interface
97                //              that should not be returned to calling code. That's what the
98                //              `promise` is for. See `dojo/promise/Promise`.
99                // canceler: Function?
100                //              Will be invoked if the deferred is canceled. The canceler
101                //              receives the reason the deferred was canceled as its argument.
102                //              The deferred is rejected with its return value, or a new
103                //              `dojo/errors/CancelError` instance.
104
105                // promise: dojo/promise/Promise
106                //              The public promise object that clients can add callbacks to.
107                var promise = this.promise = new Promise();
108
109                var deferred = this;
110                var fulfilled, result, rejection;
111                var canceled = false;
112                var waiting = [];
113
114                if(has("config-deferredInstrumentation") && Error.captureStackTrace){
115                        Error.captureStackTrace(deferred, Deferred);
116                        Error.captureStackTrace(promise, Deferred);
117                }
118
119                this.isResolved = promise.isResolved = function(){
120                        // summary:
121                        //              Checks whether the deferred has been resolved.
122                        // returns: Boolean
123
124                        return fulfilled === RESOLVED;
125                };
126
127                this.isRejected = promise.isRejected = function(){
128                        // summary:
129                        //              Checks whether the deferred has been rejected.
130                        // returns: Boolean
131
132                        return fulfilled === REJECTED;
133                };
134
135                this.isFulfilled = promise.isFulfilled = function(){
136                        // summary:
137                        //              Checks whether the deferred has been resolved or rejected.
138                        // returns: Boolean
139
140                        return !!fulfilled;
141                };
142
143                this.isCanceled = promise.isCanceled = function(){
144                        // summary:
145                        //              Checks whether the deferred has been canceled.
146                        // returns: Boolean
147
148                        return canceled;
149                };
150
151                this.progress = function(update, strict){
152                        // summary:
153                        //              Emit a progress update on the deferred.
154                        // description:
155                        //              Emit a progress update on the deferred. Progress updates
156                        //              can be used to communicate updates about the asynchronous
157                        //              operation before it has finished.
158                        // update: any
159                        //              The progress update. Passed to progbacks.
160                        // strict: Boolean?
161                        //              If strict, will throw an error if the deferred has already
162                        //              been fulfilled and consequently no progress can be emitted.
163                        // returns: dojo/promise/Promise
164                        //              Returns the original promise for the deferred.
165
166                        if(!fulfilled){
167                                signalWaiting(waiting, PROGRESS, update, null, deferred);
168                                return promise;
169                        }else if(strict === true){
170                                throw new Error(FULFILLED_ERROR_MESSAGE);
171                        }else{
172                                return promise;
173                        }
174                };
175
176                this.resolve = function(value, strict){
177                        // summary:
178                        //              Resolve the deferred.
179                        // description:
180                        //              Resolve the deferred, putting it in a success state.
181                        // value: any
182                        //              The result of the deferred. Passed to callbacks.
183                        // strict: Boolean?
184                        //              If strict, will throw an error if the deferred has already
185                        //              been fulfilled and consequently cannot be resolved.
186                        // returns: dojo/promise/Promise
187                        //              Returns the original promise for the deferred.
188
189                        if(!fulfilled){
190                                // Set fulfilled, store value. After signaling waiting listeners unset
191                                // waiting.
192                                signalWaiting(waiting, fulfilled = RESOLVED, result = value, null, deferred);
193                                waiting = null;
194                                return promise;
195                        }else if(strict === true){
196                                throw new Error(FULFILLED_ERROR_MESSAGE);
197                        }else{
198                                return promise;
199                        }
200                };
201
202                var reject = this.reject = function(error, strict){
203                        // summary:
204                        //              Reject the deferred.
205                        // description:
206                        //              Reject the deferred, putting it in an error state.
207                        // error: any
208                        //              The error result of the deferred. Passed to errbacks.
209                        // strict: Boolean?
210                        //              If strict, will throw an error if the deferred has already
211                        //              been fulfilled and consequently cannot be rejected.
212                        // returns: dojo/promise/Promise
213                        //              Returns the original promise for the deferred.
214
215                        if(!fulfilled){
216                                if(has("config-deferredInstrumentation") && Error.captureStackTrace){
217                                        Error.captureStackTrace(rejection = {}, reject);
218                                }
219                                signalWaiting(waiting, fulfilled = REJECTED, result = error, rejection, deferred);
220                                waiting = null;
221                                return promise;
222                        }else if(strict === true){
223                                throw new Error(FULFILLED_ERROR_MESSAGE);
224                        }else{
225                                return promise;
226                        }
227                };
228
229                this.then = promise.then = function(callback, errback, progback){
230                        // summary:
231                        //              Add new callbacks to the deferred.
232                        // description:
233                        //              Add new callbacks to the deferred. Callbacks can be added
234                        //              before or after the deferred is fulfilled.
235                        // callback: Function?
236                        //              Callback to be invoked when the promise is resolved.
237                        //              Receives the resolution value.
238                        // errback: Function?
239                        //              Callback to be invoked when the promise is rejected.
240                        //              Receives the rejection error.
241                        // progback: Function?
242                        //              Callback to be invoked when the promise emits a progress
243                        //              update. Receives the progress update.
244                        // returns: dojo/promise/Promise
245                        //              Returns a new promise for the result of the callback(s).
246                        //              This can be used for chaining many asynchronous operations.
247
248                        var listener = [progback, callback, errback];
249                        // Ensure we cancel the promise we're waiting for, or if callback/errback
250                        // have returned a promise, cancel that one.
251                        listener.cancel = promise.cancel;
252                        listener.deferred = new Deferred(function(reason){
253                                // Check whether cancel is really available, returned promises are not
254                                // required to expose `cancel`
255                                return listener.cancel && listener.cancel(reason);
256                        });
257                        if(fulfilled && !waiting){
258                                signalListener(listener, fulfilled, result, rejection);
259                        }else{
260                                waiting.push(listener);
261                        }
262                        return listener.deferred.promise;
263                };
264
265                this.cancel = promise.cancel = function(reason, strict){
266                        // summary:
267                        //              Inform the deferred it may cancel its asynchronous operation.
268                        // description:
269                        //              Inform the deferred it may cancel its asynchronous operation.
270                        //              The deferred's (optional) canceler is invoked and the
271                        //              deferred will be left in a rejected state. Can affect other
272                        //              promises that originate with the same deferred.
273                        // reason: any
274                        //              A message that may be sent to the deferred's canceler,
275                        //              explaining why it's being canceled.
276                        // strict: Boolean?
277                        //              If strict, will throw an error if the deferred has already
278                        //              been fulfilled and consequently cannot be canceled.
279                        // returns: any
280                        //              Returns the rejection reason if the deferred was canceled
281                        //              normally.
282
283                        if(!fulfilled){
284                                // Cancel can be called even after the deferred is fulfilled
285                                if(canceler){
286                                        var returnedReason = canceler(reason);
287                                        reason = typeof returnedReason === "undefined" ? reason : returnedReason;
288                                }
289                                canceled = true;
290                                if(!fulfilled){
291                                        // Allow canceler to provide its own reason, but fall back to a CancelError
292                                        if(typeof reason === "undefined"){
293                                                reason = new CancelError();
294                                        }
295                                        reject(reason);
296                                        return reason;
297                                }else if(fulfilled === REJECTED && result === reason){
298                                        return reason;
299                                }
300                        }else if(strict === true){
301                                throw new Error(FULFILLED_ERROR_MESSAGE);
302                        }
303                };
304
305                freezeObject(promise);
306        };
307
308        Deferred.prototype.toString = function(){
309                // returns: String
310                //              Returns `[object Deferred]`.
311
312                return "[object Deferred]";
313        };
314
315        if(instrumentation){
316                instrumentation(Deferred);
317        }
318
319        return Deferred;
320});
Note: See TracBrowser for help on using the repository browser.