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

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

Added Dojo 1.9.3 release.

File size: 12.8 KB
Line 
1define([
2        "./kernel",
3        "../Deferred",
4        "../promise/Promise",
5        "../errors/CancelError",
6        "../has",
7        "./lang",
8        "../when"
9], function(dojo, NewDeferred, Promise, CancelError, has, lang, when){
10        // module:
11        //              dojo/_base/Deferred
12
13        var mutator = function(){};
14        var freeze = Object.freeze || function(){};
15        // A deferred provides an API for creating and resolving a promise.
16        var Deferred = dojo.Deferred = function(/*Function?*/ canceller){
17                // summary:
18                //              Deprecated.   This module defines the legacy dojo/_base/Deferred API.
19                //              New code should use dojo/Deferred instead.
20                // description:
21                //              The Deferred API is based on the concept of promises that provide a
22                //              generic interface into the eventual completion of an asynchronous action.
23                //              The motivation for promises fundamentally is about creating a
24                //              separation of concerns that allows one to achieve the same type of
25                //              call patterns and logical data flow in asynchronous code as can be
26                //              achieved in synchronous code. Promises allows one
27                //              to be able to call a function purely with arguments needed for
28                //              execution, without conflating the call with concerns of whether it is
29                //              sync or async. One shouldn't need to alter a call's arguments if the
30                //              implementation switches from sync to async (or vice versa). By having
31                //              async functions return promises, the concerns of making the call are
32                //              separated from the concerns of asynchronous interaction (which are
33                //              handled by the promise).
34                //
35                //              The Deferred is a type of promise that provides methods for fulfilling the
36                //              promise with a successful result or an error. The most important method for
37                //              working with Dojo's promises is the then() method, which follows the
38                //              CommonJS proposed promise API. An example of using a Dojo promise:
39                //
40                //              |       var resultingPromise = someAsyncOperation.then(function(result){
41                //              |               ... handle result ...
42                //              |       },
43                //              |       function(error){
44                //              |               ... handle error ...
45                //              |       });
46                //
47                //              The .then() call returns a new promise that represents the result of the
48                //              execution of the callback. The callbacks will never affect the original promises value.
49                //
50                //              The Deferred instances also provide the following functions for backwards compatibility:
51                //
52                //              - addCallback(handler)
53                //              - addErrback(handler)
54                //              - callback(result)
55                //              - errback(result)
56                //
57                //              Callbacks are allowed to return promises themselves, so
58                //              you can build complicated sequences of events with ease.
59                //
60                //              The creator of the Deferred may specify a canceller.  The canceller
61                //              is a function that will be called if Deferred.cancel is called
62                //              before the Deferred fires. You can use this to implement clean
63                //              aborting of an XMLHttpRequest, etc. Note that cancel will fire the
64                //              deferred with a CancelledError (unless your canceller returns
65                //              another kind of error), so the errbacks should be prepared to
66                //              handle that error for cancellable Deferreds.
67                // example:
68                //      |       var deferred = new Deferred();
69                //      |       setTimeout(function(){ deferred.callback({success: true}); }, 1000);
70                //      |       return deferred;
71                // example:
72                //              Deferred objects are often used when making code asynchronous. It
73                //              may be easiest to write functions in a synchronous manner and then
74                //              split code using a deferred to trigger a response to a long-lived
75                //              operation. For example, instead of register a callback function to
76                //              denote when a rendering operation completes, the function can
77                //              simply return a deferred:
78                //
79                //              |       // callback style:
80                //              |       function renderLotsOfData(data, callback){
81                //              |               var success = false
82                //              |               try{
83                //              |                       for(var x in data){
84                //              |                               renderDataitem(data[x]);
85                //              |                       }
86                //              |                       success = true;
87                //              |               }catch(e){ }
88                //              |               if(callback){
89                //              |                       callback(success);
90                //              |               }
91                //              |       }
92                //
93                //              |       // using callback style
94                //              |       renderLotsOfData(someDataObj, function(success){
95                //              |               // handles success or failure
96                //              |               if(!success){
97                //              |                       promptUserToRecover();
98                //              |               }
99                //              |       });
100                //              |       // NOTE: no way to add another callback here!!
101                // example:
102                //              Using a Deferred doesn't simplify the sending code any, but it
103                //              provides a standard interface for callers and senders alike,
104                //              providing both with a simple way to service multiple callbacks for
105                //              an operation and freeing both sides from worrying about details
106                //              such as "did this get called already?". With Deferreds, new
107                //              callbacks can be added at any time.
108                //
109                //              |       // Deferred style:
110                //              |       function renderLotsOfData(data){
111                //              |               var d = new Deferred();
112                //              |               try{
113                //              |                       for(var x in data){
114                //              |                               renderDataitem(data[x]);
115                //              |                       }
116                //              |                       d.callback(true);
117                //              |               }catch(e){
118                //              |                       d.errback(new Error("rendering failed"));
119                //              |               }
120                //              |               return d;
121                //              |       }
122                //
123                //              |       // using Deferred style
124                //              |       renderLotsOfData(someDataObj).then(null, function(){
125                //              |               promptUserToRecover();
126                //              |       });
127                //              |       // NOTE: addErrback and addCallback both return the Deferred
128                //              |       // again, so we could chain adding callbacks or save the
129                //              |       // deferred for later should we need to be notified again.
130                // example:
131                //              In this example, renderLotsOfData is synchronous and so both
132                //              versions are pretty artificial. Putting the data display on a
133                //              timeout helps show why Deferreds rock:
134                //
135                //              |       // Deferred style and async func
136                //              |       function renderLotsOfData(data){
137                //              |               var d = new Deferred();
138                //              |               setTimeout(function(){
139                //              |                       try{
140                //              |                               for(var x in data){
141                //              |                                       renderDataitem(data[x]);
142                //              |                               }
143                //              |                               d.callback(true);
144                //              |                       }catch(e){
145                //              |                               d.errback(new Error("rendering failed"));
146                //              |                       }
147                //              |               }, 100);
148                //              |               return d;
149                //              |       }
150                //
151                //              |       // using Deferred style
152                //              |       renderLotsOfData(someDataObj).then(null, function(){
153                //              |               promptUserToRecover();
154                //              |       });
155                //
156                //              Note that the caller doesn't have to change his code at all to
157                //              handle the asynchronous case.
158
159                var result, finished, canceled, fired, isError, head, nextListener;
160                var promise = (this.promise = new Promise());
161
162                function complete(value){
163                        if(finished){
164                                throw new Error("This deferred has already been resolved");
165                        }
166                        result = value;
167                        finished = true;
168                        notify();
169                }
170                function notify(){
171                        var mutated;
172                        while(!mutated && nextListener){
173                                var listener = nextListener;
174                                nextListener = nextListener.next;
175                                if((mutated = (listener.progress == mutator))){ // assignment and check
176                                        finished = false;
177                                }
178
179                                var func = (isError ? listener.error : listener.resolved);
180                                if(has("config-useDeferredInstrumentation")){
181                                        if(isError && NewDeferred.instrumentRejected){
182                                                NewDeferred.instrumentRejected(result, !!func);
183                                        }
184                                }
185                                if(func){
186                                        try{
187                                                var newResult = func(result);
188                                                if (newResult && typeof newResult.then === "function"){
189                                                        newResult.then(lang.hitch(listener.deferred, "resolve"), lang.hitch(listener.deferred, "reject"), lang.hitch(listener.deferred, "progress"));
190                                                        continue;
191                                                }
192                                                var unchanged = mutated && newResult === undefined;
193                                                if(mutated && !unchanged){
194                                                        isError = newResult instanceof Error;
195                                                }
196                                                listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult);
197                                        }catch(e){
198                                                listener.deferred.reject(e);
199                                        }
200                                }else{
201                                        if(isError){
202                                                listener.deferred.reject(result);
203                                        }else{
204                                                listener.deferred.resolve(result);
205                                        }
206                                }
207                        }
208                }
209
210                this.isResolved = promise.isResolved = function(){
211                        // summary:
212                        //              Checks whether the deferred has been resolved.
213                        // returns: Boolean
214
215                        return fired == 0;
216                };
217
218                this.isRejected = promise.isRejected = function(){
219                        // summary:
220                        //              Checks whether the deferred has been rejected.
221                        // returns: Boolean
222
223                        return fired == 1;
224                };
225
226                this.isFulfilled = promise.isFulfilled = function(){
227                        // summary:
228                        //              Checks whether the deferred has been resolved or rejected.
229                        // returns: Boolean
230
231                        return fired >= 0;
232                };
233
234                this.isCanceled = promise.isCanceled = function(){
235                        // summary:
236                        //              Checks whether the deferred has been canceled.
237                        // returns: Boolean
238
239                        return canceled;
240                };
241
242                // calling resolve will resolve the promise
243                this.resolve = this.callback = function(value){
244                        // summary:
245                        //              Fulfills the Deferred instance successfully with the provide value
246                        this.fired = fired = 0;
247                        this.results = [value, null];
248                        complete(value);
249                };
250
251
252                // calling error will indicate that the promise failed
253                this.reject = this.errback = function(error){
254                        // summary:
255                        //              Fulfills the Deferred instance as an error with the provided error
256                        isError = true;
257                        this.fired = fired = 1;
258                        if(has("config-useDeferredInstrumentation")){
259                                if(NewDeferred.instrumentRejected){
260                                        NewDeferred.instrumentRejected(error, !!nextListener);
261                                }
262                        }
263                        complete(error);
264                        this.results = [null, error];
265                };
266                // call progress to provide updates on the progress on the completion of the promise
267                this.progress = function(update){
268                        // summary:
269                        //              Send progress events to all listeners
270                        var listener = nextListener;
271                        while(listener){
272                                var progress = listener.progress;
273                                progress && progress(update);
274                                listener = listener.next;
275                        }
276                };
277                this.addCallbacks = function(callback, errback){
278                        // summary:
279                        //              Adds callback and error callback for this deferred instance.
280                        // callback: Function?
281                        //              The callback attached to this deferred object.
282                        // errback: Function?
283                        //              The error callback attached to this deferred object.
284                        // returns:
285                        //              Returns this deferred object.
286                        this.then(callback, errback, mutator);
287                        return this;    // Deferred
288                };
289                // provide the implementation of the promise
290                promise.then = this.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){
291                        // summary:
292                        //              Adds a fulfilledHandler, errorHandler, and progressHandler to be called for
293                        //              completion of a promise. The fulfilledHandler is called when the promise
294                        //              is fulfilled. The errorHandler is called when a promise fails. The
295                        //              progressHandler is called for progress events. All arguments are optional
296                        //              and non-function values are ignored. The progressHandler is not only an
297                        //              optional argument, but progress events are purely optional. Promise
298                        //              providers are not required to ever create progress events.
299                        //
300                        //              This function will return a new promise that is fulfilled when the given
301                        //              fulfilledHandler or errorHandler callback is finished. This allows promise
302                        //              operations to be chained together. The value returned from the callback
303                        //              handler is the fulfillment value for the returned promise. If the callback
304                        //              throws an error, the returned promise will be moved to failed state.
305                        //
306                        // returns:
307                        //              Returns a new promise that represents the result of the
308                        //              execution of the callback. The callbacks will never affect the original promises value.
309                        // example:
310                        //              An example of using a CommonJS compliant promise:
311                        //              |       asyncComputeTheAnswerToEverything().
312                        //              |               then(addTwo).
313                        //              |               then(printResult, onError);
314                        //              |       >44
315                        //
316                        var returnDeferred = progressCallback == mutator ? this : new Deferred(promise.cancel);
317                        var listener = {
318                                resolved: resolvedCallback,
319                                error: errorCallback,
320                                progress: progressCallback,
321                                deferred: returnDeferred
322                        };
323                        if(nextListener){
324                                head = head.next = listener;
325                        }
326                        else{
327                                nextListener = head = listener;
328                        }
329                        if(finished){
330                                notify();
331                        }
332                        return returnDeferred.promise; // Promise
333                };
334                var deferred = this;
335                promise.cancel = this.cancel = function(){
336                        // summary:
337                        //              Cancels the asynchronous operation
338                        if(!finished){
339                                var error = canceller && canceller(deferred);
340                                if(!finished){
341                                        if (!(error instanceof Error)){
342                                                error = new CancelError(error);
343                                        }
344                                        error.log = false;
345                                        deferred.reject(error);
346                                }
347                        }
348                        canceled = true;
349                };
350                freeze(promise);
351        };
352        lang.extend(Deferred, {
353                addCallback: function(/*Function*/ callback){
354                        // summary:
355                        //              Adds successful callback for this deferred instance.
356                        // returns:
357                        //              Returns this deferred object.
358                        return this.addCallbacks(lang.hitch.apply(dojo, arguments));    // Deferred
359                },
360
361                addErrback: function(/*Function*/ errback){
362                        // summary:
363                        //              Adds error callback for this deferred instance.
364                        // returns:
365                        //              Returns this deferred object.
366                        return this.addCallbacks(null, lang.hitch.apply(dojo, arguments));      // Deferred
367                },
368
369                addBoth: function(/*Function*/ callback){
370                        // summary:
371                        //              Add handler as both successful callback and error callback for this deferred instance.
372                        // returns:
373                        //              Returns this deferred object.
374                        var enclosed = lang.hitch.apply(dojo, arguments);
375                        return this.addCallbacks(enclosed, enclosed);   // Deferred
376                },
377                fired: -1
378        });
379
380        Deferred.when = dojo.when = when;
381
382        return Deferred;
383});
Note: See TracBrowser for help on using the repository browser.