summaryrefslogtreecommitdiff
path: root/lib/dojo/_base/Deferred.js
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-03-04 19:02:28 +0300
committerAndrew Dolgov <[email protected]>2011-03-04 19:02:59 +0300
commita089699c8915636ba4f158d77dba9b012bc93208 (patch)
treeb2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/_base/Deferred.js
parentcfad9259a6feacfa8194b1312770ae6db1ecce50 (diff)
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/_base/Deferred.js')
-rw-r--r--lib/dojo/_base/Deferred.js446
1 files changed, 329 insertions, 117 deletions
diff --git a/lib/dojo/_base/Deferred.js b/lib/dojo/_base/Deferred.js
index dfbabc4c0..3193024ab 100644
--- a/lib/dojo/_base/Deferred.js
+++ b/lib/dojo/_base/Deferred.js
@@ -5,126 +5,338 @@
*/
-if(!dojo._hasResource["dojo._base.Deferred"]){
-dojo._hasResource["dojo._base.Deferred"]=true;
+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");
+
(function(){
-var _1=function(){
-};
-var _2=Object.freeze||function(){
-};
-dojo.Deferred=function(_3){
-var _4,_5,_6,_7,_8;
-var _9=this.promise={};
-function _a(_b){
-if(_5){
-throw new Error("This deferred has already been resolved");
-}
-_4=_b;
-_5=true;
-_c();
-};
-function _c(){
-var _d;
-while(!_d&&_8){
-var _e=_8;
-_8=_8.next;
-if(_d=(_e.progress==_1)){
-_5=false;
-}
-var _f=(_6?_e.error:_e.resolved);
-if(_f){
-try{
-var _10=_f(_4);
-if(_10&&typeof _10.then==="function"){
-_10.then(dojo.hitch(_e.deferred,"resolve"),dojo.hitch(_e.deferred,"reject"));
-continue;
-}
-var _11=_d&&_10===undefined;
-_e.deferred[_11&&_6?"reject":"resolve"](_11?_4:_10);
-}
-catch(e){
-_e.deferred.reject(e);
-}
-}else{
-if(_6){
-_e.deferred.reject(_4);
-}else{
-_e.deferred.resolve(_4);
-}
-}
-}
-};
-this.resolve=this.callback=function(_12){
-this.fired=0;
-this.results=[_12,null];
-_a(_12);
-};
-this.reject=this.errback=function(_13){
-_6=true;
-this.fired=1;
-_a(_13);
-this.results=[null,_13];
-if(!_13||_13.log!==false){
-(dojo.config.deferredOnError||function(x){
-console.error(x);
-})(_13);
-}
-};
-this.progress=function(_14){
-var _15=_8;
-while(_15){
-var _16=_15.progress;
-_16&&_16(_14);
-_15=_15.next;
-}
-};
-this.addCallbacks=function(_17,_18){
-this.then(_17,_18,_1);
-return this;
-};
-this.then=_9.then=function(_19,_1a,_1b){
-var _1c=_1b==_1?this:new dojo.Deferred(_9.cancel);
-var _1d={resolved:_19,error:_1a,progress:_1b,deferred:_1c};
-if(_8){
-_7=_7.next=_1d;
-}else{
-_8=_7=_1d;
-}
-if(_5){
-_c();
-}
-return _1c.promise;
-};
-var _1e=this;
-this.cancel=_9.cancel=function(){
-if(!_5){
-var _1f=_3&&_3(_1e);
-if(!_5){
-if(!(_1f instanceof Error)){
-_1f=new Error(_1f);
-}
-_1f.log=false;
-_1e.reject(_1f);
-}
-}
-};
-_2(_9);
-};
-dojo.extend(dojo.Deferred,{addCallback:function(_20){
-return this.addCallbacks(dojo.hitch.apply(dojo,arguments));
-},addErrback:function(_21){
-return this.addCallbacks(null,dojo.hitch.apply(dojo,arguments));
-},addBoth:function(_22){
-var _23=dojo.hitch.apply(dojo,arguments);
-return this.addCallbacks(_23,_23);
-},fired:-1});
+ var mutator = function(){};
+ var freeze = Object.freeze || function(){};
+ // A deferred provides an API for creating and resolving a promise.
+ dojo.Deferred = function(/*Function?*/canceller){
+ // summary:
+ // Deferreds provide a generic means for encapsulating an asynchronous
+ // operation and notifying users of the completion and result of the operation.
+ // description:
+ // The dojo.Deferred API is based on the concept of promises that provide a
+ // generic interface into the eventual completion of an asynchronous action.
+ // The motivation for promises fundamentally is about creating a
+ // separation of concerns that allows one to achieve the same type of
+ // call patterns and logical data flow in asynchronous code as can be
+ // achieved in synchronous code. Promises allows one
+ // to be able to call a function purely with arguments needed for
+ // execution, without conflating the call with concerns of whether it is
+ // sync or async. One shouldn't need to alter a call's arguments if the
+ // implementation switches from sync to async (or vice versa). By having
+ // async functions return promises, the concerns of making the call are
+ // separated from the concerns of asynchronous interaction (which are
+ // handled by the promise).
+ //
+ // The dojo.Deferred is a type of promise that provides methods for fulfilling the
+ // promise with a successful result or an error. The most important method for
+ // working with Dojo's promises is the then() method, which follows the
+ // CommonJS proposed promise API. An example of using a Dojo promise:
+ //
+ // | var resultingPromise = someAsyncOperation.then(function(result){
+ // | ... handle result ...
+ // | },
+ // | function(error){
+ // | ... handle error ...
+ // | });
+ //
+ // The .then() call returns a new promise that represents the result of the
+ // execution of the callback. The callbacks will never affect the original promises value.
+ //
+ // The dojo.Deferred instances also provide the following functions for backwards compatibility:
+ //
+ // * addCallback(handler)
+ // * addErrback(handler)
+ // * callback(result)
+ // * errback(result)
+ //
+ // Callbacks are allowed to return promisesthemselves, 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).then(null, 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).then(null, function(){
+ // | promptUserToRecover();
+ // | });
+ //
+ // Note that the caller doesn't have to change his code at all to
+ // handle the asynchronous case.
+ var result, finished, isError, head, nextListener;
+ var promise = this.promise = {};
+
+ function complete(value){
+ if(finished){
+ throw new Error("This deferred has already been resolved");
+ }
+ result = value;
+ finished = true;
+ notify();
+ }
+ function notify(){
+ var mutated;
+ while(!mutated && nextListener){
+ var listener = nextListener;
+ nextListener = nextListener.next;
+ if(mutated = (listener.progress == mutator)){ // assignment and check
+ finished = false;
+ }
+ var func = (isError ? listener.error : listener.resolved);
+ if (func) {
+ try {
+ var newResult = func(result);
+ if (newResult && typeof newResult.then === "function") {
+ newResult.then(dojo.hitch(listener.deferred, "resolve"), dojo.hitch(listener.deferred, "reject"));
+ continue;
+ }
+ var unchanged = mutated && newResult === undefined;
+ listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult);
+ }
+ catch (e) {
+ listener.deferred.reject(e);
+ }
+ }else {
+ if(isError){
+ listener.deferred.reject(result);
+ }else{
+ listener.deferred.resolve(result);
+ }
+ }
+ }
+ }
+ // calling resolve will resolve the promise
+ this.resolve = this.callback = function(value){
+ // summary:
+ // Fulfills the Deferred instance successfully with the provide value
+ this.fired = 0;
+ this.results = [value, null];
+ complete(value);
+ };
+
+
+ // calling error will indicate that the promise failed
+ this.reject = this.errback = function(error){
+ // summary:
+ // Fulfills the Deferred instance as an error with the provided error
+ isError = true;
+ this.fired = 1;
+ complete(error);
+ this.results = [null, error];
+ if(!error || error.log !== false){
+ (dojo.config.deferredOnError || function(x){ console.error(x); })(error);
+ }
+ };
+ // call progress to provide updates on the progress on the completion of the promise
+ this.progress = function(update){
+ // summary
+ // Send progress events to all listeners
+ var listener = nextListener;
+ while(listener){
+ var progress = listener.progress;
+ progress && progress(update);
+ listener = listener.next;
+ }
+ };
+ this.addCallbacks = function(/*Function?*/callback, /*Function?*/errback){
+ this.then(callback, errback, mutator);
+ return this;
+ };
+ // provide the implementation of the promise
+ this.then = promise.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){
+ // summary
+ // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for
+ // completion of a promise. The fulfilledHandler is called when the promise
+ // is fulfilled. The errorHandler is called when a promise fails. The
+ // progressHandler is called for progress events. All arguments are optional
+ // and non-function values are ignored. The progressHandler is not only an
+ // optional argument, but progress events are purely optional. Promise
+ // providers are not required to ever create progress events.
+ //
+ // This function will return a new promise that is fulfilled when the given
+ // fulfilledHandler or errorHandler callback is finished. This allows promise
+ // operations to be chained together. The value returned from the callback
+ // handler is the fulfillment value for the returned promise. If the callback
+ // throws an error, the returned promise will be moved to failed state.
+ //
+ // example:
+ // An example of using a CommonJS compliant promise:
+ // | asyncComputeTheAnswerToEverything().
+ // | then(addTwo).
+ // | then(printResult, onError);
+ // | >44
+ //
+ var returnDeferred = progressCallback == mutator ? this : new dojo.Deferred(promise.cancel);
+ var listener = {
+ resolved: resolvedCallback,
+ error: errorCallback,
+ progress: progressCallback,
+ deferred: returnDeferred
+ };
+ if(nextListener){
+ head = head.next = listener;
+ }
+ else{
+ nextListener = head = listener;
+ }
+ if(finished){
+ notify();
+ }
+ return returnDeferred.promise;
+ };
+ var deferred = this;
+ this.cancel = promise.cancel = function () {
+ // summary:
+ // Cancels the asynchronous operation
+ if(!finished){
+ var error = canceller && canceller(deferred);
+ if(!finished){
+ if (!(error instanceof Error)) {
+ error = new Error(error);
+ }
+ error.log = false;
+ deferred.reject(error);
+ }
+ }
+ }
+ freeze(promise);
+ };
+ dojo.extend(dojo.Deferred, {
+ addCallback: function (/*Function*/callback) {
+ return this.addCallbacks(dojo.hitch.apply(dojo, arguments));
+ },
+
+ addErrback: function (/*Function*/errback) {
+ return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments));
+ },
+
+ addBoth: function (/*Function*/callback) {
+ var enclosed = dojo.hitch.apply(dojo, arguments);
+ return this.addCallbacks(enclosed, enclosed);
+ },
+ fired: -1
+ });
})();
-dojo.when=function(_24,_25,_26,_27){
-if(_24&&typeof _24.then==="function"){
-return _24.then(_25,_26,_27);
-}
-return _25(_24);
+dojo.when = function(promiseOrValue, /*Function?*/callback, /*Function?*/errback, /*Function?*/progressHandler){
+ // summary:
+ // This provides normalization between normal synchronous values and
+ // asynchronous promises, so you can interact with them in a common way
+ // example:
+ // | function printFirstAndList(items){
+ // | dojo.when(findFirst(items), console.log);
+ // | dojo.when(findLast(items), console.log);
+ // | }
+ // | function findFirst(items){
+ // | return dojo.when(items, function(items){
+ // | return items[0];
+ // | });
+ // | }
+ // | function findLast(items){
+ // | return dojo.when(items, function(items){
+ // | return items[items.length];
+ // | });
+ // | }
+ // And now all three of his functions can be used sync or async.
+ // | printFirstAndLast([1,2,3,4]) will work just as well as
+ // | printFirstAndLast(dojo.xhrGet(...));
+
+ if(promiseOrValue && typeof promiseOrValue.then === "function"){
+ return promiseOrValue.then(callback, errback, progressHandler);
+ }
+ return callback(promiseOrValue);
};
+
}