Page Menu
Home
Code
Search
Configure Global Search
Log In
Files
F392589
Deferred.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Subscribers
None
Deferred.js
View Options
if
(
!
dojo
.
_hasResource
[
"dojo._base.Deferred"
]){
//_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo
.
_hasResource
[
"dojo._base.Deferred"
]
=
true
;
dojo
.
provide
(
"dojo._base.Deferred"
);
dojo
.
require
(
"dojo._base.lang"
);
dojo
.
Deferred
=
function
(
/*Function?*/
canceller
){
// summary:
// Encapsulates a sequence of callbacks in response to a value that
// may not yet be available. This is modeled after the Deferred class
// from Twisted <http://twistedmatrix.com>.
// description:
// JavaScript has no threads, and even if it did, threads are hard.
// Deferreds are a way of abstracting non-blocking events, such as the
// final response to an XMLHttpRequest. Deferreds create a promise to
// return a response a some point in the future and an easy way to
// register your interest in receiving that response.
//
// The most important methods for Deffered users are:
//
// * addCallback(handler)
// * addErrback(handler)
// * callback(result)
// * errback(result)
//
// In general, when a function returns a Deferred, users then "fill
// in" the second half of the contract by registering callbacks and
// error handlers. You may register as many callback and errback
// handlers as you like and they will be executed in the order
// registered when a result is provided. Usually this result is
// provided as the result of an asynchronous operation. The code
// "managing" the Deferred (the code that made the promise to provide
// an answer later) will use the callback() and errback() methods to
// communicate with registered listeners about the result of the
// operation. At this time, all registered result handlers are called
// *with the most recent result value*.
//
// Deferred callback handlers are treated as a chain, and each item in
// the chain is required to return a value that will be fed into
// successive handlers. The most minimal callback may be registered
// like this:
//
// | var d = new dojo.Deferred();
// | d.addCallback(function(result){ return result; });
//
// Perhaps the most common mistake when first using Deferreds is to
// forget to return a value (in most cases, the value you were
// passed).
//
// The sequence of callbacks is internally represented as a list of
// 2-tuples containing the callback/errback pair. For example, the
// following call sequence:
//
// | var d = new dojo.Deferred();
// | d.addCallback(myCallback);
// | d.addErrback(myErrback);
// | d.addBoth(myBoth);
// | d.addCallbacks(myCallback, myErrback);
//
// is translated into a Deferred with the following internal
// representation:
//
// | [
// | [myCallback, null],
// | [null, myErrback],
// | [myBoth, myBoth],
// | [myCallback, myErrback]
// | ]
//
// The Deferred also keeps track of its current status (fired). Its
// status may be one of three things:
//
// * -1: no value yet (initial condition)
// * 0: success
// * 1: error
//
// A Deferred will be in the error state if one of the following three
// conditions are met:
//
// 1. The result given to callback or errback is "instanceof" Error
// 2. The previous callback or errback raised an exception while
// executing
// 3. The previous callback or errback returned a value
// "instanceof" Error
//
// Otherwise, the Deferred will be in the success state. The state of
// the Deferred determines the next element in the callback sequence
// to run.
//
// When a callback or errback occurs with the example deferred chain,
// something equivalent to the following will happen (imagine
// that exceptions are caught and returned):
//
// | // d.callback(result) or d.errback(result)
// | if(!(result instanceof Error)){
// | result = myCallback(result);
// | }
// | if(result instanceof Error){
// | result = myErrback(result);
// | }
// | result = myBoth(result);
// | if(result instanceof Error){
// | result = myErrback(result);
// | }else{
// | result = myCallback(result);
// | }
//
// The result is then stored away in case another step is added to the
// callback sequence. Since the Deferred already has a value
// available, any new callbacks added will be called immediately.
//
// There are two other "advanced" details about this implementation
// that are useful:
//
// Callbacks are allowed to return Deferred instances themselves, so
// you can build complicated sequences of events with ease.
//
// The creator of the Deferred may specify a canceller. The canceller
// is a function that will be called if Deferred.cancel is called
// before the Deferred fires. You can use this to implement clean
// aborting of an XMLHttpRequest, etc. Note that cancel will fire the
// deferred with a CancelledError (unless your canceller returns
// another kind of error), so the errbacks should be prepared to
// handle that error for cancellable Deferreds.
// example:
// | var deferred = new dojo.Deferred();
// | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
// | return deferred;
// example:
// Deferred objects are often used when making code asynchronous. It
// may be easiest to write functions in a synchronous manner and then
// split code using a deferred to trigger a response to a long-lived
// operation. For example, instead of register a callback function to
// denote when a rendering operation completes, the function can
// simply return a deferred:
//
// | // callback style:
// | function renderLotsOfData(data, callback){
// | var success = false
// | try{
// | for(var x in data){
// | renderDataitem(data[x]);
// | }
// | success = true;
// | }catch(e){ }
// | if(callback){
// | callback(success);
// | }
// | }
//
// | // using callback style
// | renderLotsOfData(someDataObj, function(success){
// | // handles success or failure
// | if(!success){
// | promptUserToRecover();
// | }
// | });
// | // NOTE: no way to add another callback here!!
// example:
// Using a Deferred doesn't simplify the sending code any, but it
// provides a standard interface for callers and senders alike,
// providing both with a simple way to service multiple callbacks for
// an operation and freeing both sides from worrying about details
// such as "did this get called already?". With Deferreds, new
// callbacks can be added at any time.
//
// | // Deferred style:
// | function renderLotsOfData(data){
// | var d = new dojo.Deferred();
// | try{
// | for(var x in data){
// | renderDataitem(data[x]);
// | }
// | d.callback(true);
// | }catch(e){
// | d.errback(new Error("rendering failed"));
// | }
// | return d;
// | }
//
// | // using Deferred style
// | renderLotsOfData(someDataObj).addErrback(function(){
// | promptUserToRecover();
// | });
// | // NOTE: addErrback and addCallback both return the Deferred
// | // again, so we could chain adding callbacks or save the
// | // deferred for later should we need to be notified again.
// example:
// In this example, renderLotsOfData is syncrhonous and so both
// versions are pretty artificial. Putting the data display on a
// timeout helps show why Deferreds rock:
//
// | // Deferred style and async func
// | function renderLotsOfData(data){
// | var d = new dojo.Deferred();
// | setTimeout(function(){
// | try{
// | for(var x in data){
// | renderDataitem(data[x]);
// | }
// | d.callback(true);
// | }catch(e){
// | d.errback(new Error("rendering failed"));
// | }
// | }, 100);
// | return d;
// | }
//
// | // using Deferred style
// | renderLotsOfData(someDataObj).addErrback(function(){
// | promptUserToRecover();
// | });
//
// Note that the caller doesn't have to change his code at all to
// handle the asynchronous case.
this
.
chain
=
[];
this
.
id
=
this
.
_nextId
();
this
.
fired
=
-
1
;
this
.
paused
=
0
;
this
.
results
=
[
null
,
null
];
this
.
canceller
=
canceller
;
this
.
silentlyCancelled
=
false
;
};
dojo
.
extend
(
dojo
.
Deferred
,
{
/*
makeCalled: function(){
// summary:
// returns a new, empty deferred, which is already in the called
// state. Calling callback() or errback() on this deferred will
// yeild an error and adding new handlers to it will result in
// them being called immediately.
var deferred = new dojo.Deferred();
deferred.callback();
return deferred;
},
toString: function(){
var state;
if(this.fired == -1){
state = 'unfired';
}else{
state = this.fired ? 'success' : 'error';
}
return 'Deferred(' + this.id + ', ' + state + ')';
},
*/
_nextId
:
(
function
(){
var
n
=
1
;
return
function
(){
return
n
++
;
};
})(),
cancel
:
function
(){
// summary:
// Cancels a Deferred that has not yet received a value, or is
// waiting on another Deferred as its value.
// description:
// If a canceller is defined, the canceller is called. If the
// canceller did not return an error, or there was no canceller,
// then the errback chain is started.
var
err
;
if
(
this
.
fired
==
-
1
){
if
(
this
.
canceller
){
err
=
this
.
canceller
(
this
);
}
else
{
this
.
silentlyCancelled
=
true
;
}
if
(
this
.
fired
==
-
1
){
if
(
!
(
err
instanceof
Error
)){
var
res
=
err
;
err
=
new
Error
(
"Deferred Cancelled"
);
err
.
dojoType
=
"cancel"
;
err
.
cancelResult
=
res
;
}
this
.
errback
(
err
);
}
}
else
if
(
(
this
.
fired
==
0
)
&&
(
this
.
results
[
0
]
instanceof
dojo
.
Deferred
)
){
this
.
results
[
0
].
cancel
();
}
},
_resback
:
function
(
res
){
// summary:
// The private primitive that means either callback or errback
this
.
fired
=
((
res
instanceof
Error
)
?
1
:
0
);
this
.
results
[
this
.
fired
]
=
res
;
this
.
_fire
();
},
_check
:
function
(){
if
(
this
.
fired
!=
-
1
){
if
(
!
this
.
silentlyCancelled
){
throw
new
Error
(
"already called!"
);
}
this
.
silentlyCancelled
=
false
;
return
;
}
},
callback
:
function
(
res
){
// summary: Begin the callback sequence with a non-error value.
/*
callback or errback should only be called once on a given
Deferred.
*/
this
.
_check
();
this
.
_resback
(
res
);
},
errback
:
function
(
/*Error*/
res
){
// summary:
// Begin the callback sequence with an error result.
this
.
_check
();
if
(
!
(
res
instanceof
Error
)){
res
=
new
Error
(
res
);
}
this
.
_resback
(
res
);
},
addBoth
:
function
(
/*Function||Object*/
cb
,
/*Optional, String*/
cbfn
){
// summary:
// Add the same function as both a callback and an errback as the
// next element on the callback sequence. This is useful for code
// that you want to guarantee to run, e.g. a finalizer.
var
enclosed
=
dojo
.
hitch
(
cb
,
cbfn
);
if
(
arguments
.
length
>
2
){
enclosed
=
dojo
.
partial
(
enclosed
,
arguments
,
2
);
}
return
this
.
addCallbacks
(
enclosed
,
enclosed
);
},
addCallback
:
function
(
cb
,
cbfn
){
// summary:
// Add a single callback to the end of the callback sequence.
var
enclosed
=
dojo
.
hitch
(
cb
,
cbfn
);
if
(
arguments
.
length
>
2
){
enclosed
=
dojo
.
partial
(
enclosed
,
arguments
,
2
);
}
return
this
.
addCallbacks
(
enclosed
,
null
);
},
addErrback
:
function
(
cb
,
cbfn
){
// summary:
// Add a single callback to the end of the callback sequence.
var
enclosed
=
dojo
.
hitch
(
cb
,
cbfn
);
if
(
arguments
.
length
>
2
){
enclosed
=
dojo
.
partial
(
enclosed
,
arguments
,
2
);
}
return
this
.
addCallbacks
(
null
,
enclosed
);
},
addCallbacks
:
function
(
cb
,
eb
){
// summary:
// Add separate callback and errback to the end of the callback
// sequence.
this
.
chain
.
push
([
cb
,
eb
])
if
(
this
.
fired
>=
0
){
this
.
_fire
();
}
return
this
;
},
_fire
:
function
(){
// summary:
// Used internally to exhaust the callback sequence when a result
// is available.
var
chain
=
this
.
chain
;
var
fired
=
this
.
fired
;
var
res
=
this
.
results
[
fired
];
var
self
=
this
;
var
cb
=
null
;
while
(
(
chain
.
length
>
0
)
&&
(
this
.
paused
==
0
)
){
// Array
var
f
=
chain
.
shift
()[
fired
];
if
(
!
f
){
continue
;
}
try
{
res
=
f
(
res
);
fired
=
((
res
instanceof
Error
)
?
1
:
0
);
if
(
res
instanceof
dojo
.
Deferred
){
cb
=
function
(
res
){
self
.
_resback
(
res
);
// inlined from _pause()
self
.
paused
--
;
if
(
(
self
.
paused
==
0
)
&&
(
self
.
fired
>=
0
)
){
self
.
_fire
();
}
}
// inlined from _unpause
this
.
paused
++
;
}
}
catch
(
err
){
console
.
debug
(
err
);
fired
=
1
;
res
=
err
;
}
}
this
.
fired
=
fired
;
this
.
results
[
fired
]
=
res
;
if
((
cb
)
&&
(
this
.
paused
)){
// this is for "tail recursion" in case the dependent
// deferred is already fired
res
.
addBoth
(
cb
);
}
}
});
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Feb 22, 20:29 (2 d, 13 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25657
Default Alt Text
Deferred.js (12 KB)
Attached To
rZEDHG ZedLegacy
Event Timeline
Log In to Comment