diff options
author | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:28 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:59 +0300 |
commit | a089699c8915636ba4f158d77dba9b012bc93208 (patch) | |
tree | b2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/_base/Deferred.js | |
parent | cfad9259a6feacfa8194b1312770ae6db1ecce50 (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.js | 446 |
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); }; + } |