Deferred.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. define("dojo/_base/Deferred", ["./kernel", "./lang"], function(dojo, lang){
  2. // module:
  3. // dojo/_base/Deferred
  4. // summary:
  5. // This module defines dojo.Deferred.
  6. var mutator = function(){};
  7. var freeze = Object.freeze || function(){};
  8. // A deferred provides an API for creating and resolving a promise.
  9. dojo.Deferred = function(/*Function?*/ canceller){
  10. // summary:
  11. // Deferreds provide a generic means for encapsulating an asynchronous
  12. // operation and notifying users of the completion and result of the operation.
  13. // description:
  14. // The dojo.Deferred API is based on the concept of promises that provide a
  15. // generic interface into the eventual completion of an asynchronous action.
  16. // The motivation for promises fundamentally is about creating a
  17. // separation of concerns that allows one to achieve the same type of
  18. // call patterns and logical data flow in asynchronous code as can be
  19. // achieved in synchronous code. Promises allows one
  20. // to be able to call a function purely with arguments needed for
  21. // execution, without conflating the call with concerns of whether it is
  22. // sync or async. One shouldn't need to alter a call's arguments if the
  23. // implementation switches from sync to async (or vice versa). By having
  24. // async functions return promises, the concerns of making the call are
  25. // separated from the concerns of asynchronous interaction (which are
  26. // handled by the promise).
  27. //
  28. // The dojo.Deferred is a type of promise that provides methods for fulfilling the
  29. // promise with a successful result or an error. The most important method for
  30. // working with Dojo's promises is the then() method, which follows the
  31. // CommonJS proposed promise API. An example of using a Dojo promise:
  32. //
  33. // | var resultingPromise = someAsyncOperation.then(function(result){
  34. // | ... handle result ...
  35. // | },
  36. // | function(error){
  37. // | ... handle error ...
  38. // | });
  39. //
  40. // The .then() call returns a new promise that represents the result of the
  41. // execution of the callback. The callbacks will never affect the original promises value.
  42. //
  43. // The dojo.Deferred instances also provide the following functions for backwards compatibility:
  44. //
  45. // * addCallback(handler)
  46. // * addErrback(handler)
  47. // * callback(result)
  48. // * errback(result)
  49. //
  50. // Callbacks are allowed to return promises themselves, so
  51. // you can build complicated sequences of events with ease.
  52. //
  53. // The creator of the Deferred may specify a canceller. The canceller
  54. // is a function that will be called if Deferred.cancel is called
  55. // before the Deferred fires. You can use this to implement clean
  56. // aborting of an XMLHttpRequest, etc. Note that cancel will fire the
  57. // deferred with a CancelledError (unless your canceller returns
  58. // another kind of error), so the errbacks should be prepared to
  59. // handle that error for cancellable Deferreds.
  60. // example:
  61. // | var deferred = new dojo.Deferred();
  62. // | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
  63. // | return deferred;
  64. // example:
  65. // Deferred objects are often used when making code asynchronous. It
  66. // may be easiest to write functions in a synchronous manner and then
  67. // split code using a deferred to trigger a response to a long-lived
  68. // operation. For example, instead of register a callback function to
  69. // denote when a rendering operation completes, the function can
  70. // simply return a deferred:
  71. //
  72. // | // callback style:
  73. // | function renderLotsOfData(data, callback){
  74. // | var success = false
  75. // | try{
  76. // | for(var x in data){
  77. // | renderDataitem(data[x]);
  78. // | }
  79. // | success = true;
  80. // | }catch(e){ }
  81. // | if(callback){
  82. // | callback(success);
  83. // | }
  84. // | }
  85. //
  86. // | // using callback style
  87. // | renderLotsOfData(someDataObj, function(success){
  88. // | // handles success or failure
  89. // | if(!success){
  90. // | promptUserToRecover();
  91. // | }
  92. // | });
  93. // | // NOTE: no way to add another callback here!!
  94. // example:
  95. // Using a Deferred doesn't simplify the sending code any, but it
  96. // provides a standard interface for callers and senders alike,
  97. // providing both with a simple way to service multiple callbacks for
  98. // an operation and freeing both sides from worrying about details
  99. // such as "did this get called already?". With Deferreds, new
  100. // callbacks can be added at any time.
  101. //
  102. // | // Deferred style:
  103. // | function renderLotsOfData(data){
  104. // | var d = new dojo.Deferred();
  105. // | try{
  106. // | for(var x in data){
  107. // | renderDataitem(data[x]);
  108. // | }
  109. // | d.callback(true);
  110. // | }catch(e){
  111. // | d.errback(new Error("rendering failed"));
  112. // | }
  113. // | return d;
  114. // | }
  115. //
  116. // | // using Deferred style
  117. // | renderLotsOfData(someDataObj).then(null, function(){
  118. // | promptUserToRecover();
  119. // | });
  120. // | // NOTE: addErrback and addCallback both return the Deferred
  121. // | // again, so we could chain adding callbacks or save the
  122. // | // deferred for later should we need to be notified again.
  123. // example:
  124. // In this example, renderLotsOfData is synchronous and so both
  125. // versions are pretty artificial. Putting the data display on a
  126. // timeout helps show why Deferreds rock:
  127. //
  128. // | // Deferred style and async func
  129. // | function renderLotsOfData(data){
  130. // | var d = new dojo.Deferred();
  131. // | setTimeout(function(){
  132. // | try{
  133. // | for(var x in data){
  134. // | renderDataitem(data[x]);
  135. // | }
  136. // | d.callback(true);
  137. // | }catch(e){
  138. // | d.errback(new Error("rendering failed"));
  139. // | }
  140. // | }, 100);
  141. // | return d;
  142. // | }
  143. //
  144. // | // using Deferred style
  145. // | renderLotsOfData(someDataObj).then(null, function(){
  146. // | promptUserToRecover();
  147. // | });
  148. //
  149. // Note that the caller doesn't have to change his code at all to
  150. // handle the asynchronous case.
  151. var result, finished, isError, head, nextListener;
  152. var promise = (this.promise = {});
  153. function complete(value){
  154. if(finished){
  155. throw new Error("This deferred has already been resolved");
  156. }
  157. result = value;
  158. finished = true;
  159. notify();
  160. }
  161. function notify(){
  162. var mutated;
  163. while(!mutated && nextListener){
  164. var listener = nextListener;
  165. nextListener = nextListener.next;
  166. if((mutated = (listener.progress == mutator))){ // assignment and check
  167. finished = false;
  168. }
  169. var func = (isError ? listener.error : listener.resolved);
  170. if(func){
  171. try{
  172. var newResult = func(result);
  173. if (newResult && typeof newResult.then === "function"){
  174. newResult.then(lang.hitch(listener.deferred, "resolve"), lang.hitch(listener.deferred, "reject"), lang.hitch(listener.deferred, "progress"));
  175. continue;
  176. }
  177. var unchanged = mutated && newResult === undefined;
  178. if(mutated && !unchanged){
  179. isError = newResult instanceof Error;
  180. }
  181. listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult);
  182. }catch(e){
  183. listener.deferred.reject(e);
  184. }
  185. }else{
  186. if(isError){
  187. listener.deferred.reject(result);
  188. }else{
  189. listener.deferred.resolve(result);
  190. }
  191. }
  192. }
  193. }
  194. // calling resolve will resolve the promise
  195. this.resolve = this.callback = function(value){
  196. // summary:
  197. // Fulfills the Deferred instance successfully with the provide value
  198. this.fired = 0;
  199. this.results = [value, null];
  200. complete(value);
  201. };
  202. // calling error will indicate that the promise failed
  203. this.reject = this.errback = function(error){
  204. // summary:
  205. // Fulfills the Deferred instance as an error with the provided error
  206. isError = true;
  207. this.fired = 1;
  208. complete(error);
  209. this.results = [null, error];
  210. if(!error || error.log !== false){
  211. (dojo.config.deferredOnError || function(x){ console.error(x); })(error);
  212. }
  213. };
  214. // call progress to provide updates on the progress on the completion of the promise
  215. this.progress = function(update){
  216. // summary:
  217. // Send progress events to all listeners
  218. var listener = nextListener;
  219. while(listener){
  220. var progress = listener.progress;
  221. progress && progress(update);
  222. listener = listener.next;
  223. }
  224. };
  225. this.addCallbacks = function(callback, errback){
  226. // summary:
  227. // Adds callback and error callback for this deferred instance.
  228. // callback: Function?
  229. // The callback attached to this deferred object.
  230. // errback: Function?
  231. // The error callback attached to this deferred object.
  232. // returns:
  233. // Returns this deferred object.
  234. this.then(callback, errback, mutator);
  235. return this; // dojo.Deferred
  236. };
  237. // provide the implementation of the promise
  238. promise.then = this.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){
  239. // summary:
  240. // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for
  241. // completion of a promise. The fulfilledHandler is called when the promise
  242. // is fulfilled. The errorHandler is called when a promise fails. The
  243. // progressHandler is called for progress events. All arguments are optional
  244. // and non-function values are ignored. The progressHandler is not only an
  245. // optional argument, but progress events are purely optional. Promise
  246. // providers are not required to ever create progress events.
  247. //
  248. // This function will return a new promise that is fulfilled when the given
  249. // fulfilledHandler or errorHandler callback is finished. This allows promise
  250. // operations to be chained together. The value returned from the callback
  251. // handler is the fulfillment value for the returned promise. If the callback
  252. // throws an error, the returned promise will be moved to failed state.
  253. //
  254. // returns:
  255. // Returns a new promise that represents the result of the
  256. // execution of the callback. The callbacks will never affect the original promises value.
  257. // example:
  258. // An example of using a CommonJS compliant promise:
  259. // | asyncComputeTheAnswerToEverything().
  260. // | then(addTwo).
  261. // | then(printResult, onError);
  262. // | >44
  263. //
  264. var returnDeferred = progressCallback == mutator ? this : new dojo.Deferred(promise.cancel);
  265. var listener = {
  266. resolved: resolvedCallback,
  267. error: errorCallback,
  268. progress: progressCallback,
  269. deferred: returnDeferred
  270. };
  271. if(nextListener){
  272. head = head.next = listener;
  273. }
  274. else{
  275. nextListener = head = listener;
  276. }
  277. if(finished){
  278. notify();
  279. }
  280. return returnDeferred.promise; // Promise
  281. };
  282. var deferred = this;
  283. promise.cancel = this.cancel = function (){
  284. // summary:
  285. // Cancels the asynchronous operation
  286. if(!finished){
  287. var error = canceller && canceller(deferred);
  288. if(!finished){
  289. if (!(error instanceof Error)){
  290. error = new Error(error);
  291. }
  292. error.log = false;
  293. deferred.reject(error);
  294. }
  295. }
  296. };
  297. freeze(promise);
  298. };
  299. lang.extend(dojo.Deferred, {
  300. addCallback: function (/*Function*/ callback){
  301. // summary:
  302. // Adds successful callback for this deferred instance.
  303. // returns:
  304. // Returns this deferred object.
  305. return this.addCallbacks(lang.hitch.apply(dojo, arguments)); // dojo.Deferred
  306. },
  307. addErrback: function (/*Function*/ errback){
  308. // summary:
  309. // Adds error callback for this deferred instance.
  310. // returns:
  311. // Returns this deferred object.
  312. return this.addCallbacks(null, lang.hitch.apply(dojo, arguments)); // dojo.Deferred
  313. },
  314. addBoth: function (/*Function*/ callback){
  315. // summary:
  316. // Add handler as both successful callback and error callback for this deferred instance.
  317. // returns:
  318. // Returns this deferred object.
  319. var enclosed = lang.hitch.apply(dojo, arguments);
  320. return this.addCallbacks(enclosed, enclosed); // dojo.Deferred
  321. },
  322. fired: -1
  323. });
  324. dojo.Deferred.when = dojo.when = function(promiseOrValue, /*Function?*/ callback, /*Function?*/ errback, /*Function?*/ progressHandler){
  325. // summary:
  326. // This provides normalization between normal synchronous values and
  327. // asynchronous promises, so you can interact with them in a common way
  328. // returns:
  329. // Returns a new promise that represents the result of the execution of callback
  330. // when parameter "promiseOrValue" is promise.
  331. // Returns the execution result of callback when parameter "promiseOrValue" is value.
  332. // example:
  333. // | function printFirstAndLast(items){
  334. // | dojo.when(findFirst(items), console.log);
  335. // | dojo.when(findLast(items), console.log);
  336. // | }
  337. // | function findFirst(items){
  338. // | return dojo.when(items, function(items){
  339. // | return items[0];
  340. // | });
  341. // | }
  342. // | function findLast(items){
  343. // | return dojo.when(items, function(items){
  344. // | return items[items.length - 1];
  345. // | });
  346. // | }
  347. // And now all three of his functions can be used sync or async.
  348. // | printFirstAndLast([1,2,3,4]) will work just as well as
  349. // | printFirstAndLast(dojo.xhrGet(...));
  350. if(promiseOrValue && typeof promiseOrValue.then === "function"){
  351. return promiseOrValue.then(callback, errback, progressHandler);
  352. }
  353. return callback ? callback(promiseOrValue) : promiseOrValue; // Promise
  354. };
  355. return dojo.Deferred;
  356. });