]> git.wh0rd.org - tt-rss.git/blame - lib/dojo/_base/Deferred.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dojo / _base / Deferred.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("dojo/_base/Deferred", [
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, 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 // calling resolve will resolve the promise
210 this.resolve = this.callback = function(value){
211 // summary:
212 // Fulfills the Deferred instance successfully with the provide value
213 this.fired = 0;
214 this.results = [value, null];
215 complete(value);
216 };
217
218
219 // calling error will indicate that the promise failed
220 this.reject = this.errback = function(error){
221 // summary:
222 // Fulfills the Deferred instance as an error with the provided error
223 isError = true;
224 this.fired = 1;
225 if(has("config-useDeferredInstrumentation")){
226 if(NewDeferred.instrumentRejected){
227 NewDeferred.instrumentRejected(error, !!nextListener);
228 }
229 }
230 complete(error);
231 this.results = [null, error];
232 };
233 // call progress to provide updates on the progress on the completion of the promise
234 this.progress = function(update){
235 // summary:
236 // Send progress events to all listeners
237 var listener = nextListener;
238 while(listener){
239 var progress = listener.progress;
240 progress && progress(update);
241 listener = listener.next;
242 }
243 };
244 this.addCallbacks = function(callback, errback){
245 // summary:
246 // Adds callback and error callback for this deferred instance.
247 // callback: Function?
248 // The callback attached to this deferred object.
249 // errback: Function?
250 // The error callback attached to this deferred object.
251 // returns:
252 // Returns this deferred object.
253 this.then(callback, errback, mutator);
254 return this; // Deferred
255 };
256 // provide the implementation of the promise
257 promise.then = this.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){
258 // summary:
259 // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for
260 // completion of a promise. The fulfilledHandler is called when the promise
261 // is fulfilled. The errorHandler is called when a promise fails. The
262 // progressHandler is called for progress events. All arguments are optional
263 // and non-function values are ignored. The progressHandler is not only an
264 // optional argument, but progress events are purely optional. Promise
265 // providers are not required to ever create progress events.
266 //
267 // This function will return a new promise that is fulfilled when the given
268 // fulfilledHandler or errorHandler callback is finished. This allows promise
269 // operations to be chained together. The value returned from the callback
270 // handler is the fulfillment value for the returned promise. If the callback
271 // throws an error, the returned promise will be moved to failed state.
272 //
273 // returns:
274 // Returns a new promise that represents the result of the
275 // execution of the callback. The callbacks will never affect the original promises value.
276 // example:
277 // An example of using a CommonJS compliant promise:
278 // | asyncComputeTheAnswerToEverything().
279 // | then(addTwo).
280 // | then(printResult, onError);
281 // | >44
282 //
283 var returnDeferred = progressCallback == mutator ? this : new Deferred(promise.cancel);
284 var listener = {
285 resolved: resolvedCallback,
286 error: errorCallback,
287 progress: progressCallback,
288 deferred: returnDeferred
289 };
290 if(nextListener){
291 head = head.next = listener;
292 }
293 else{
294 nextListener = head = listener;
295 }
296 if(finished){
297 notify();
298 }
299 return returnDeferred.promise; // Promise
300 };
301 var deferred = this;
302 promise.cancel = this.cancel = function(){
303 // summary:
304 // Cancels the asynchronous operation
305 if(!finished){
306 var error = canceller && canceller(deferred);
307 if(!finished){
308 if (!(error instanceof Error)){
309 error = new CancelError(error);
310 }
311 error.log = false;
312 deferred.reject(error);
313 }
314 }
315 };
316 freeze(promise);
317 };
318 lang.extend(Deferred, {
319 addCallback: function(/*Function*/ callback){
320 // summary:
321 // Adds successful callback for this deferred instance.
322 // returns:
323 // Returns this deferred object.
324 return this.addCallbacks(lang.hitch.apply(dojo, arguments)); // Deferred
325 },
326
327 addErrback: function(/*Function*/ errback){
328 // summary:
329 // Adds error callback for this deferred instance.
330 // returns:
331 // Returns this deferred object.
332 return this.addCallbacks(null, lang.hitch.apply(dojo, arguments)); // Deferred
333 },
334
335 addBoth: function(/*Function*/ callback){
336 // summary:
337 // Add handler as both successful callback and error callback for this deferred instance.
338 // returns:
339 // Returns this deferred object.
340 var enclosed = lang.hitch.apply(dojo, arguments);
341 return this.addCallbacks(enclosed, enclosed); // Deferred
342 },
343 fired: -1
344 });
345
346 Deferred.when = dojo.when = when;
347
348 return Deferred;
349});