]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/_base/Deferred.js.uncompressed.js
1 define("dojo/_base/Deferred", [
5 "../errors/CancelError",
9 ], function(dojo
, NewDeferred
, Promise
, CancelError
, has
, lang
, when
){
11 // dojo/_base/Deferred
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
){
18 // Deprecated. This module defines the legacy dojo/_base/Deferred API.
19 // New code should use dojo/Deferred instead.
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).
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:
40 // | var resultingPromise = someAsyncOperation.then(function(result){
41 // | ... handle result ...
44 // | ... handle error ...
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.
50 // The Deferred instances also provide the following functions for backwards compatibility:
52 // - addCallback(handler)
53 // - addErrback(handler)
57 // Callbacks are allowed to return promises themselves, so
58 // you can build complicated sequences of events with ease.
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.
68 // | var deferred = new Deferred();
69 // | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
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:
79 // | // callback style:
80 // | function renderLotsOfData(data, callback){
81 // | var success = false
83 // | for(var x in data){
84 // | renderDataitem(data[x]);
89 // | callback(success);
93 // | // using callback style
94 // | renderLotsOfData(someDataObj, function(success){
95 // | // handles success or failure
97 // | promptUserToRecover();
100 // | // NOTE: no way to add another callback here!!
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.
109 // | // Deferred style:
110 // | function renderLotsOfData(data){
111 // | var d = new Deferred();
113 // | for(var x in data){
114 // | renderDataitem(data[x]);
116 // | d.callback(true);
118 // | d.errback(new Error("rendering failed"));
123 // | // using Deferred style
124 // | renderLotsOfData(someDataObj).then(null, function(){
125 // | promptUserToRecover();
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.
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:
135 // | // Deferred style and async func
136 // | function renderLotsOfData(data){
137 // | var d = new Deferred();
138 // | setTimeout(function(){
140 // | for(var x in data){
141 // | renderDataitem(data[x]);
143 // | d.callback(true);
145 // | d.errback(new Error("rendering failed"));
151 // | // using Deferred style
152 // | renderLotsOfData(someDataObj).then(null, function(){
153 // | promptUserToRecover();
156 // Note that the caller doesn't have to change his code at all to
157 // handle the asynchronous case.
159 var result
, finished
, isError
, head
, nextListener
;
160 var promise
= (this.promise
= new Promise());
162 function complete(value
){
164 throw new Error("This deferred has already been resolved");
172 while(!mutated
&& nextListener
){
173 var listener
= nextListener
;
174 nextListener
= nextListener
.next
;
175 if((mutated
= (listener
.progress
== mutator
))){ // assignment and check
179 var func
= (isError
? listener
.error
: listener
.resolved
);
180 if(has("config-useDeferredInstrumentation")){
181 if(isError
&& NewDeferred
.instrumentRejected
){
182 NewDeferred
.instrumentRejected(result
, !!func
);
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"));
192 var unchanged
= mutated
&& newResult
=== undefined;
193 if(mutated
&& !unchanged
){
194 isError
= newResult
instanceof Error
;
196 listener
.deferred
[unchanged
&& isError
? "reject" : "resolve"](unchanged
? result
: newResult
);
198 listener
.deferred
.reject(e
);
202 listener
.deferred
.reject(result
);
204 listener
.deferred
.resolve(result
);
209 // calling resolve will resolve the promise
210 this.resolve
= this.callback = function(value
){
212 // Fulfills the Deferred instance successfully with the provide value
214 this.results
= [value
, null];
219 // calling error will indicate that the promise failed
220 this.reject
= this.errback = function(error
){
222 // Fulfills the Deferred instance as an error with the provided error
225 if(has("config-useDeferredInstrumentation")){
226 if(NewDeferred
.instrumentRejected
){
227 NewDeferred
.instrumentRejected(error
, !!nextListener
);
231 this.results
= [null, error
];
233 // call progress to provide updates on the progress on the completion of the promise
234 this.progress = function(update
){
236 // Send progress events to all listeners
237 var listener
= nextListener
;
239 var progress
= listener
.progress
;
240 progress
&& progress(update
);
241 listener
= listener
.next
;
244 this.addCallbacks = function(callback
, errback
){
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.
252 // Returns this deferred object.
253 this.then(callback
, errback
, mutator
);
254 return this; // Deferred
256 // provide the implementation of the promise
257 promise
.then
= this.then = function(/*Function?*/resolvedCallback
, /*Function?*/errorCallback
, /*Function?*/progressCallback
){
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.
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.
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.
277 // An example of using a CommonJS compliant promise:
278 // | asyncComputeTheAnswerToEverything().
280 // | then(printResult, onError);
283 var returnDeferred
= progressCallback
== mutator
? this : new Deferred(promise
.cancel
);
285 resolved
: resolvedCallback
,
286 error
: errorCallback
,
287 progress
: progressCallback
,
288 deferred
: returnDeferred
291 head
= head
.next
= listener
;
294 nextListener
= head
= listener
;
299 return returnDeferred
.promise
; // Promise
302 promise
.cancel
= this.cancel = function(){
304 // Cancels the asynchronous operation
306 var error
= canceller
&& canceller(deferred
);
308 if (!(error
instanceof Error
)){
309 error
= new CancelError(error
);
312 deferred
.reject(error
);
318 lang
.extend(Deferred
, {
319 addCallback: function(/*Function*/ callback
){
321 // Adds successful callback for this deferred instance.
323 // Returns this deferred object.
324 return this.addCallbacks(lang
.hitch
.apply(dojo
, arguments
)); // Deferred
327 addErrback: function(/*Function*/ errback
){
329 // Adds error callback for this deferred instance.
331 // Returns this deferred object.
332 return this.addCallbacks(null, lang
.hitch
.apply(dojo
, arguments
)); // Deferred
335 addBoth: function(/*Function*/ callback
){
337 // Add handler as both successful callback and error callback for this deferred instance.
339 // Returns this deferred object.
340 var enclosed
= lang
.hitch
.apply(dojo
, arguments
);
341 return this.addCallbacks(enclosed
, enclosed
); // Deferred
346 Deferred
.when
= dojo
.when
= when
;