8fa9184b2591eef2d968fdd538ca4354803866b4.svn-base 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. if (!dojo._hasResource["dojo._base.Deferred"]) { // _hasResource checks added
  2. // by build. Do not use
  3. // _hasResource directly in
  4. // your code.
  5. dojo._hasResource["dojo._base.Deferred"] = true;
  6. dojo.provide("dojo._base.Deferred");
  7. dojo.require("dojo._base.lang");
  8. dojo.Deferred = function(/* Function? */canceller) {
  9. // summary:
  10. // Encapsulates a sequence of callbacks in response to a value that
  11. // may not yet be available. This is modeled after the Deferred class
  12. // from Twisted <http://twistedmatrix.com>.
  13. // description:
  14. // JavaScript has no threads, and even if it did, threads are hard.
  15. // Deferreds are a way of abstracting non-blocking events, such as the
  16. // final response to an XMLHttpRequest. Deferreds create a promise to
  17. // return a response a some point in the future and an easy way to
  18. // register your interest in receiving that response.
  19. //
  20. // The most important methods for Deffered users are:
  21. //
  22. // * addCallback(handler)
  23. // * addErrback(handler)
  24. // * callback(result)
  25. // * errback(result)
  26. //
  27. // In general, when a function returns a Deferred, users then "fill
  28. // in" the second half of the contract by registering callbacks and
  29. // error handlers. You may register as many callback and errback
  30. // handlers as you like and they will be executed in the order
  31. // registered when a result is provided. Usually this result is
  32. // provided as the result of an asynchronous operation. The code
  33. // "managing" the Deferred (the code that made the promise to provide
  34. // an answer later) will use the callback() and errback() methods to
  35. // communicate with registered listeners about the result of the
  36. // operation. At this time, all registered result handlers are called
  37. // *with the most recent result value*.
  38. //
  39. // Deferred callback handlers are treated as a chain, and each item in
  40. // the chain is required to return a value that will be fed into
  41. // successive handlers. The most minimal callback may be registered
  42. // like this:
  43. //
  44. // | var d = new dojo.Deferred();
  45. // | d.addCallback(function(result){ return result; });
  46. //
  47. // Perhaps the most common mistake when first using Deferreds is to
  48. // forget to return a value (in most cases, the value you were
  49. // passed).
  50. //
  51. // The sequence of callbacks is internally represented as a list of
  52. // 2-tuples containing the callback/errback pair. For example, the
  53. // following call sequence:
  54. //
  55. // | var d = new dojo.Deferred();
  56. // | d.addCallback(myCallback);
  57. // | d.addErrback(myErrback);
  58. // | d.addBoth(myBoth);
  59. // | d.addCallbacks(myCallback, myErrback);
  60. //
  61. // is translated into a Deferred with the following internal
  62. // representation:
  63. //
  64. // | [
  65. // | [myCallback, null],
  66. // | [null, myErrback],
  67. // | [myBoth, myBoth],
  68. // | [myCallback, myErrback]
  69. // | ]
  70. //
  71. // The Deferred also keeps track of its current status (fired). Its
  72. // status may be one of three things:
  73. //
  74. // * -1: no value yet (initial condition)
  75. // * 0: success
  76. // * 1: error
  77. //
  78. // A Deferred will be in the error state if one of the following three
  79. // conditions are met:
  80. //
  81. // 1. The result given to callback or errback is "instanceof" Error
  82. // 2. The previous callback or errback raised an exception while
  83. // executing
  84. // 3. The previous callback or errback returned a value
  85. // "instanceof" Error
  86. //
  87. // Otherwise, the Deferred will be in the success state. The state of
  88. // the Deferred determines the next element in the callback sequence
  89. // to run.
  90. //
  91. // When a callback or errback occurs with the example deferred chain,
  92. // something equivalent to the following will happen (imagine
  93. // that exceptions are caught and returned):
  94. //
  95. // | // d.callback(result) or d.errback(result)
  96. // | if(!(result instanceof Error)){
  97. // | result = myCallback(result);
  98. // | }
  99. // | if(result instanceof Error){
  100. // | result = myErrback(result);
  101. // | }
  102. // | result = myBoth(result);
  103. // | if(result instanceof Error){
  104. // | result = myErrback(result);
  105. // | }else{
  106. // | result = myCallback(result);
  107. // | }
  108. //
  109. // The result is then stored away in case another step is added to the
  110. // callback sequence. Since the Deferred already has a value
  111. // available, any new callbacks added will be called immediately.
  112. //
  113. // There are two other "advanced" details about this implementation
  114. // that are useful:
  115. //
  116. // Callbacks are allowed to return Deferred instances themselves, so
  117. // you can build complicated sequences of events with ease.
  118. //
  119. // The creator of the Deferred may specify a canceller. The canceller
  120. // is a function that will be called if Deferred.cancel is called
  121. // before the Deferred fires. You can use this to implement clean
  122. // aborting of an XMLHttpRequest, etc. Note that cancel will fire the
  123. // deferred with a CancelledError (unless your canceller returns
  124. // another kind of error), so the errbacks should be prepared to
  125. // handle that error for cancellable Deferreds.
  126. // example:
  127. // | var deferred = new dojo.Deferred();
  128. // | setTimeout(function(){ deferred.callback({success: true}); },
  129. // 1000);
  130. // | return deferred;
  131. // example:
  132. // Deferred objects are often used when making code asynchronous. It
  133. // may be easiest to write functions in a synchronous manner and then
  134. // split code using a deferred to trigger a response to a long-lived
  135. // operation. For example, instead of register a callback function to
  136. // denote when a rendering operation completes, the function can
  137. // simply return a deferred:
  138. //
  139. // | // callback style:
  140. // | function renderLotsOfData(data, callback){
  141. // | var success = false
  142. // | try{
  143. // | for(var x in data){
  144. // | renderDataitem(data[x]);
  145. // | }
  146. // | success = true;
  147. // | }catch(e){ }
  148. // | if(callback){
  149. // | callback(success);
  150. // | }
  151. // | }
  152. //
  153. // | // using callback style
  154. // | renderLotsOfData(someDataObj, function(success){
  155. // | // handles success or failure
  156. // | if(!success){
  157. // | promptUserToRecover();
  158. // | }
  159. // | });
  160. // | // NOTE: no way to add another callback here!!
  161. // example:
  162. // Using a Deferred doesn't simplify the sending code any, but it
  163. // provides a standard interface for callers and senders alike,
  164. // providing both with a simple way to service multiple callbacks for
  165. // an operation and freeing both sides from worrying about details
  166. // such as "did this get called already?". With Deferreds, new
  167. // callbacks can be added at any time.
  168. //
  169. // | // Deferred style:
  170. // | function renderLotsOfData(data){
  171. // | var d = new dojo.Deferred();
  172. // | try{
  173. // | for(var x in data){
  174. // | renderDataitem(data[x]);
  175. // | }
  176. // | d.callback(true);
  177. // | }catch(e){
  178. // | d.errback(new Error("rendering failed"));
  179. // | }
  180. // | return d;
  181. // | }
  182. //
  183. // | // using Deferred style
  184. // | renderLotsOfData(someDataObj).addErrback(function(){
  185. // | promptUserToRecover();
  186. // | });
  187. // | // NOTE: addErrback and addCallback both return the Deferred
  188. // | // again, so we could chain adding callbacks or save the
  189. // | // deferred for later should we need to be notified again.
  190. // example:
  191. // In this example, renderLotsOfData is syncrhonous and so both
  192. // versions are pretty artificial. Putting the data display on a
  193. // timeout helps show why Deferreds rock:
  194. //
  195. // | // Deferred style and async func
  196. // | function renderLotsOfData(data){
  197. // | var d = new dojo.Deferred();
  198. // | setTimeout(function(){
  199. // | try{
  200. // | for(var x in data){
  201. // | renderDataitem(data[x]);
  202. // | }
  203. // | d.callback(true);
  204. // | }catch(e){
  205. // | d.errback(new Error("rendering failed"));
  206. // | }
  207. // | }, 100);
  208. // | return d;
  209. // | }
  210. //
  211. // | // using Deferred style
  212. // | renderLotsOfData(someDataObj).addErrback(function(){
  213. // | promptUserToRecover();
  214. // | });
  215. //
  216. // Note that the caller doesn't have to change his code at all to
  217. // handle the asynchronous case.
  218. this.chain = [];
  219. this.id = this._nextId();
  220. this.fired = -1;
  221. this.paused = 0;
  222. this.results = [null, null];
  223. this.canceller = canceller;
  224. this.silentlyCancelled = false;
  225. };
  226. dojo.extend(dojo.Deferred, {
  227. /*
  228. * makeCalled: function(){ // summary: // returns a new, empty
  229. * deferred, which is already in the called // state. Calling
  230. * callback() or errback() on this deferred will // yeild an
  231. * error and adding new handlers to it will result in // them
  232. * being called immediately. var deferred = new dojo.Deferred();
  233. * deferred.callback(); return deferred; },
  234. *
  235. * toString: function(){ var state; if(this.fired == -1){ state =
  236. * 'unfired'; }else{ state = this.fired ? 'success' : 'error'; }
  237. * return 'Deferred(' + this.id + ', ' + state + ')'; },
  238. */
  239. _nextId : (function() {
  240. var n = 1;
  241. return function() {
  242. return n++;
  243. };
  244. })(),
  245. cancel : function() {
  246. // summary:
  247. // Cancels a Deferred that has not yet received a value, or
  248. // is
  249. // waiting on another Deferred as its value.
  250. // description:
  251. // If a canceller is defined, the canceller is called. If
  252. // the
  253. // canceller did not return an error, or there was no
  254. // canceller,
  255. // then the errback chain is started.
  256. var err;
  257. if (this.fired == -1) {
  258. if (this.canceller) {
  259. err = this.canceller(this);
  260. } else {
  261. this.silentlyCancelled = true;
  262. }
  263. if (this.fired == -1) {
  264. if (!(err instanceof Error)) {
  265. var res = err;
  266. err = new Error("Deferred Cancelled");
  267. err.dojoType = "cancel";
  268. err.cancelResult = res;
  269. }
  270. this.errback(err);
  271. }
  272. } else if ((this.fired == 0)
  273. && (this.results[0] instanceof dojo.Deferred)) {
  274. this.results[0].cancel();
  275. }
  276. },
  277. _resback : function(res) {
  278. // summary:
  279. // The private primitive that means either callback or
  280. // errback
  281. this.fired = ((res instanceof Error) ? 1 : 0);
  282. this.results[this.fired] = res;
  283. this._fire();
  284. },
  285. _check : function() {
  286. if (this.fired != -1) {
  287. if (!this.silentlyCancelled) {
  288. throw new Error("already called!");
  289. }
  290. this.silentlyCancelled = false;
  291. return;
  292. }
  293. },
  294. callback : function(res) {
  295. // summary: Begin the callback sequence with a non-error
  296. // value.
  297. /*
  298. * callback or errback should only be called once on a given
  299. * Deferred.
  300. */
  301. this._check();
  302. this._resback(res);
  303. },
  304. errback : function(/* Error */res) {
  305. // summary:
  306. // Begin the callback sequence with an error result.
  307. this._check();
  308. if (!(res instanceof Error)) {
  309. res = new Error(res);
  310. }
  311. this._resback(res);
  312. },
  313. addBoth : function(/* Function||Object */cb, /* Optional, String */
  314. cbfn) {
  315. // summary:
  316. // Add the same function as both a callback and an errback
  317. // as the
  318. // next element on the callback sequence. This is useful for
  319. // code
  320. // that you want to guarantee to run, e.g. a finalizer.
  321. var enclosed = dojo.hitch(cb, cbfn);
  322. if (arguments.length > 2) {
  323. enclosed = dojo.partial(enclosed, arguments, 2);
  324. }
  325. return this.addCallbacks(enclosed, enclosed);
  326. },
  327. addCallback : function(cb, cbfn) {
  328. // summary:
  329. // Add a single callback to the end of the callback
  330. // sequence.
  331. var enclosed = dojo.hitch(cb, cbfn);
  332. if (arguments.length > 2) {
  333. enclosed = dojo.partial(enclosed, arguments, 2);
  334. }
  335. return this.addCallbacks(enclosed, null);
  336. },
  337. addErrback : function(cb, cbfn) {
  338. // summary:
  339. // Add a single callback to the end of the callback
  340. // sequence.
  341. var enclosed = dojo.hitch(cb, cbfn);
  342. if (arguments.length > 2) {
  343. enclosed = dojo.partial(enclosed, arguments, 2);
  344. }
  345. return this.addCallbacks(null, enclosed);
  346. },
  347. addCallbacks : function(cb, eb) {
  348. // summary:
  349. // Add separate callback and errback to the end of the
  350. // callback
  351. // sequence.
  352. this.chain.push([cb, eb])
  353. if (this.fired >= 0) {
  354. this._fire();
  355. }
  356. return this;
  357. },
  358. _fire : function() {
  359. // summary:
  360. // Used internally to exhaust the callback sequence when a
  361. // result
  362. // is available.
  363. var chain = this.chain;
  364. var fired = this.fired;
  365. var res = this.results[fired];
  366. var self = this;
  367. var cb = null;
  368. while ((chain.length > 0) && (this.paused == 0)) {
  369. // Array
  370. var f = chain.shift()[fired];
  371. if (!f) {
  372. continue;
  373. }
  374. try {
  375. res = f(res);
  376. fired = ((res instanceof Error) ? 1 : 0);
  377. if (res instanceof dojo.Deferred) {
  378. cb = function(res) {
  379. self._resback(res);
  380. // inlined from _pause()
  381. self.paused--;
  382. if ((self.paused == 0) && (self.fired >= 0)) {
  383. self._fire();
  384. }
  385. }
  386. // inlined from _unpause
  387. this.paused++;
  388. }
  389. } catch (err) {
  390. console.debug(err);
  391. fired = 1;
  392. res = err;
  393. }
  394. }
  395. this.fired = fired;
  396. this.results[fired] = res;
  397. if ((cb) && (this.paused)) {
  398. // this is for "tail recursion" in case the dependent
  399. // deferred is already fired
  400. res.addBoth(cb);
  401. }
  402. }
  403. });
  404. }