Deferred.js 13 KB

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