1 if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo._base.Deferred"] = true;
3 dojo.provide("dojo._base.Deferred");
4 dojo.require("dojo._base.lang");
6 dojo.Deferred = function(/*Function?*/ canceller){
8 // Encapsulates a sequence of callbacks in response to a value that
9 // may not yet be available. This is modeled after the Deferred class
10 // from Twisted <http://twistedmatrix.com>.
12 // JavaScript has no threads, and even if it did, threads are hard.
13 // Deferreds are a way of abstracting non-blocking events, such as the
14 // final response to an XMLHttpRequest. Deferreds create a promise to
15 // return a response a some point in the future and an easy way to
16 // register your interest in receiving that response.
18 // The most important methods for Deffered users are:
20 // * addCallback(handler)
21 // * addErrback(handler)
25 // In general, when a function returns a Deferred, users then "fill
26 // in" the second half of the contract by registering callbacks and
27 // error handlers. You may register as many callback and errback
28 // handlers as you like and they will be executed in the order
29 // registered when a result is provided. Usually this result is
30 // provided as the result of an asynchronous operation. The code
31 // "managing" the Deferred (the code that made the promise to provide
32 // an answer later) will use the callback() and errback() methods to
33 // communicate with registered listeners about the result of the
34 // operation. At this time, all registered result handlers are called
35 // *with the most recent result value*.
37 // Deferred callback handlers are treated as a chain, and each item in
38 // the chain is required to return a value that will be fed into
39 // successive handlers. The most minimal callback may be registered
42 // | var d = new dojo.Deferred();
43 // | d.addCallback(function(result){ return result; });
45 // Perhaps the most common mistake when first using Deferreds is to
46 // forget to return a value (in most cases, the value you were
49 // The sequence of callbacks is internally represented as a list of
50 // 2-tuples containing the callback/errback pair. For example, the
51 // following call sequence:
53 // | var d = new dojo.Deferred();
54 // | d.addCallback(myCallback);
55 // | d.addErrback(myErrback);
56 // | d.addBoth(myBoth);
57 // | d.addCallbacks(myCallback, myErrback);
59 // is translated into a Deferred with the following internal
63 // | [myCallback, null],
64 // | [null, myErrback],
65 // | [myBoth, myBoth],
66 // | [myCallback, myErrback]
69 // The Deferred also keeps track of its current status (fired). Its
70 // status may be one of three things:
72 // * -1: no value yet (initial condition)
76 // A Deferred will be in the error state if one of the following three
77 // conditions are met:
79 // 1. The result given to callback or errback is "instanceof" Error
80 // 2. The previous callback or errback raised an exception while
82 // 3. The previous callback or errback returned a value
85 // Otherwise, the Deferred will be in the success state. The state of
86 // the Deferred determines the next element in the callback sequence
89 // When a callback or errback occurs with the example deferred chain,
90 // something equivalent to the following will happen (imagine
91 // that exceptions are caught and returned):
93 // | // d.callback(result) or d.errback(result)
94 // | if(!(result instanceof Error)){
95 // | result = myCallback(result);
97 // | if(result instanceof Error){
98 // | result = myErrback(result);
100 // | result = myBoth(result);
101 // | if(result instanceof Error){
102 // | result = myErrback(result);
104 // | result = myCallback(result);
107 // The result is then stored away in case another step is added to the
108 // callback sequence. Since the Deferred already has a value
109 // available, any new callbacks added will be called immediately.
111 // There are two other "advanced" details about this implementation
114 // Callbacks are allowed to return Deferred instances themselves, so
115 // you can build complicated sequences of events with ease.
117 // The creator of the Deferred may specify a canceller. The canceller
118 // is a function that will be called if Deferred.cancel is called
119 // before the Deferred fires. You can use this to implement clean
120 // aborting of an XMLHttpRequest, etc. Note that cancel will fire the
121 // deferred with a CancelledError (unless your canceller returns
122 // another kind of error), so the errbacks should be prepared to
123 // handle that error for cancellable Deferreds.
125 // | var deferred = new dojo.Deferred();
126 // | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
127 // | return deferred;
129 // Deferred objects are often used when making code asynchronous. It
130 // may be easiest to write functions in a synchronous manner and then
131 // split code using a deferred to trigger a response to a long-lived
132 // operation. For example, instead of register a callback function to
133 // denote when a rendering operation completes, the function can
134 // simply return a deferred:
136 // | // callback style:
137 // | function renderLotsOfData(data, callback){
138 // | var success = false
140 // | for(var x in data){
141 // | renderDataitem(data[x]);
146 // | callback(success);
150 // | // using callback style
151 // | renderLotsOfData(someDataObj, function(success){
152 // | // handles success or failure
154 // | promptUserToRecover();
157 // | // NOTE: no way to add another callback here!!
159 // Using a Deferred doesn't simplify the sending code any, but it
160 // provides a standard interface for callers and senders alike,
161 // providing both with a simple way to service multiple callbacks for
162 // an operation and freeing both sides from worrying about details
163 // such as "did this get called already?". With Deferreds, new
164 // callbacks can be added at any time.
166 // | // Deferred style:
167 // | function renderLotsOfData(data){
168 // | var d = new dojo.Deferred();
170 // | for(var x in data){
171 // | renderDataitem(data[x]);
173 // | d.callback(true);
175 // | d.errback(new Error("rendering failed"));
180 // | // using Deferred style
181 // | renderLotsOfData(someDataObj).addErrback(function(){
182 // | promptUserToRecover();
184 // | // NOTE: addErrback and addCallback both return the Deferred
185 // | // again, so we could chain adding callbacks or save the
186 // | // deferred for later should we need to be notified again.
188 // In this example, renderLotsOfData is syncrhonous and so both
189 // versions are pretty artificial. Putting the data display on a
190 // timeout helps show why Deferreds rock:
192 // | // Deferred style and async func
193 // | function renderLotsOfData(data){
194 // | var d = new dojo.Deferred();
195 // | setTimeout(function(){
197 // | for(var x in data){
198 // | renderDataitem(data[x]);
200 // | d.callback(true);
202 // | d.errback(new Error("rendering failed"));
208 // | // using Deferred style
209 // | renderLotsOfData(someDataObj).addErrback(function(){
210 // | promptUserToRecover();
213 // Note that the caller doesn't have to change his code at all to
214 // handle the asynchronous case.
217 this.id = this._nextId();
220 this.results = [null, null];
221 this.canceller = canceller;
222 this.silentlyCancelled = false;
225 dojo.extend(dojo.Deferred, {
227 makeCalled: function(){
229 // returns a new, empty deferred, which is already in the called
230 // state. Calling callback() or errback() on this deferred will
231 // yeild an error and adding new handlers to it will result in
232 // them being called immediately.
233 var deferred = new dojo.Deferred();
238 toString: function(){
240 if(this.fired == -1){
243 state = this.fired ? 'success' : 'error';
245 return 'Deferred(' + this.id + ', ' + state + ')';
249 _nextId: (function(){
251 return function(){ return n++; };
256 // Cancels a Deferred that has not yet received a value, or is
257 // waiting on another Deferred as its value.
259 // If a canceller is defined, the canceller is called. If the
260 // canceller did not return an error, or there was no canceller,
261 // then the errback chain is started.
263 if(this.fired == -1){
265 err = this.canceller(this);
267 this.silentlyCancelled = true;
269 if(this.fired == -1){
270 if(!(err instanceof Error)){
272 err = new Error("Deferred Cancelled");
273 err.dojoType = "cancel";
274 err.cancelResult = res;
278 }else if( (this.fired == 0) &&
279 (this.results[0] instanceof dojo.Deferred)
281 this.results[0].cancel();
286 _resback: function(res){
288 // The private primitive that means either callback or errback
289 this.fired = ((res instanceof Error) ? 1 : 0);
290 this.results[this.fired] = res;
295 if(this.fired != -1){
296 if(!this.silentlyCancelled){
297 throw new Error("already called!");
299 this.silentlyCancelled = false;
304 callback: function(res){
306 // Begin the callback sequence with a non-error value.
309 callback or errback should only be called once on a given
316 errback: function(/*Error*/res){
318 // Begin the callback sequence with an error result.
320 if(!(res instanceof Error)){
321 res = new Error(res);
326 addBoth: function(/*Function|Object*/cb, /*String?*/cbfn){
328 // Add the same function as both a callback and an errback as the
329 // next element on the callback sequence.This is useful for code
330 // that you want to guarantee to run, e.g. a finalizer.
331 var enclosed = dojo.hitch.apply(dojo, arguments);
332 return this.addCallbacks(enclosed, enclosed);
335 addCallback: function(/*Function|Object*/cb, /*String?*/cbfn /*...*/){
337 // Add a single callback to the end of the callback sequence.
338 return this.addCallbacks(dojo.hitch.apply(dojo, arguments));
341 addErrback: function(cb, cbfn){
343 // Add a single callback to the end of the callback sequence.
344 return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments));
347 addCallbacks: function(cb, eb){
349 // Add separate callback and errback to the end of the callback
351 this.chain.push([cb, eb])
360 // Used internally to exhaust the callback sequence when a result
362 var chain = this.chain;
363 var fired = this.fired;
364 var res = this.results[fired];
368 (chain.length > 0) &&
372 var f = chain.shift()[fired];
376 fired = ((res instanceof Error) ? 1 : 0);
377 if(res instanceof dojo.Deferred){
380 // inlined from _pause()
383 (self.paused == 0) &&
389 // inlined from _unpause
399 this.results[fired] = res;
400 if((cb)&&(this.paused)){
401 // this is for "tail recursion" in case the dependent
402 // deferred is already fired