API Docs for: 3.10.3
Show:

File: promise/js/resolver.js

  1. /**
  2. Represents an asynchronous operation. Provides a
  3. standard API for subscribing to the moment that the operation completes either
  4. successfully (`fulfill()`) or unsuccessfully (`reject()`).
  5.  
  6. @class Promise.Resolver
  7. @constructor
  8. @param {Promise} promise The promise instance this resolver will be handling
  9. **/
  10. function Resolver(promise) {
  11. /**
  12. List of success callbacks
  13.  
  14. @property _callbacks
  15. @type Array
  16. @private
  17. **/
  18. this._callbacks = [];
  19.  
  20. /**
  21. List of failure callbacks
  22.  
  23. @property _errbacks
  24. @type Array
  25. @private
  26. **/
  27. this._errbacks = [];
  28.  
  29. /**
  30. The promise for this Resolver.
  31.  
  32. @property promise
  33. @type Promise
  34. **/
  35. this.promise = promise;
  36.  
  37. /**
  38. The status of the operation. This property may take only one of the following
  39. values: 'pending', 'fulfilled' or 'rejected'.
  40.  
  41. @property _status
  42. @type String
  43. @default 'pending'
  44. @private
  45. **/
  46. this._status = 'pending';
  47. }
  48.  
  49. Y.mix(Resolver.prototype, {
  50. /**
  51. Resolves the promise, signaling successful completion of the
  52. represented operation. All "onFulfilled" subscriptions are executed and passed
  53. the value provided to this method. After calling `fulfill()`, `reject()` and
  54. `notify()` are disabled.
  55.  
  56. @method fulfill
  57. @param {Any} value Value to pass along to the "onFulfilled" subscribers
  58. **/
  59. fulfill: function (value) {
  60. if (this._status === 'pending') {
  61. this._result = value;
  62. }
  63.  
  64. if (this._status !== 'rejected') {
  65. this._notify(this._callbacks, this._result);
  66.  
  67. // Reset the callback list so that future calls to fulfill()
  68. // won't call the same callbacks again. Promises keep a list
  69. // of callbacks, they're not the same as events. In practice,
  70. // calls to fulfill() after the first one should not be made by
  71. // the user but by then()
  72. this._callbacks = [];
  73.  
  74. // Once a promise gets fulfilled it can't be rejected, so
  75. // there is no point in keeping the list. Remove it to help
  76. // garbage collection
  77. this._errbacks = null;
  78.  
  79. this._status = 'fulfilled';
  80. }
  81. },
  82.  
  83. /**
  84. Resolves the promise, signaling *un*successful completion of the
  85. represented operation. All "onRejected" subscriptions are executed with
  86. the value provided to this method. After calling `reject()`, `resolve()`
  87. and `notify()` are disabled.
  88.  
  89. @method reject
  90. @param {Any} value Value to pass along to the "reject" subscribers
  91. **/
  92. reject: function (reason) {
  93. if (this._status === 'pending') {
  94. this._result = reason;
  95. }
  96.  
  97. if (this._status !== 'fulfilled') {
  98. this._notify(this._errbacks, this._result);
  99.  
  100. // See fulfill()
  101. this._callbacks = null;
  102. this._errbacks = [];
  103.  
  104. this._status = 'rejected';
  105. }
  106. },
  107.  
  108. /**
  109. Schedule execution of a callback to either or both of "resolve" and
  110. "reject" resolutions for the Resolver. The callbacks
  111. are wrapped in a new Resolver and that Resolver's corresponding promise
  112. is returned. This allows operation chaining ala
  113. `functionA().then(functionB).then(functionC)` where `functionA` returns
  114. a promise, and `functionB` and `functionC` _may_ return promises.
  115.  
  116. @method then
  117. @param {Function} [callback] function to execute if the Resolver
  118. resolves successfully
  119. @param {Function} [errback] function to execute if the Resolver
  120. resolves unsuccessfully
  121. @return {Promise} The promise of a new Resolver wrapping the resolution
  122. of either "resolve" or "reject" callback
  123. **/
  124. then: function (callback, errback) {
  125. // When the current promise is fulfilled or rejected, either the
  126. // callback or errback will be executed via the function pushed onto
  127. // this._callbacks or this._errbacks. However, to allow then()
  128. // chaining, the execution of either function needs to be represented
  129. // by a Resolver (the same Resolver can represent both flow paths), and
  130. // its promise returned.
  131. var promise = this.promise,
  132. thenFulfill, thenReject,
  133.  
  134. // using promise constructor allows for customized promises to be
  135. // returned instead of plain ones
  136. then = new promise.constructor(function (fulfill, reject) {
  137. thenFulfill = fulfill;
  138. thenReject = reject;
  139. }),
  140.  
  141. callbackList = this._callbacks || [],
  142. errbackList = this._errbacks || [];
  143.  
  144. // Because the callback and errback are represented by a Resolver, it
  145. // must be fulfilled or rejected to propagate through the then() chain.
  146. // The same logic applies to resolve() and reject() for fulfillment.
  147. callbackList.push(typeof callback === 'function' ?
  148. this._wrap(thenFulfill, thenReject, callback) : thenFulfill);
  149. errbackList.push(typeof errback === 'function' ?
  150. this._wrap(thenFulfill, thenReject, errback) : thenReject);
  151.  
  152. // If a promise is already fulfilled or rejected, notify the newly added
  153. // callbacks by calling fulfill() or reject()
  154. if (this._status === 'fulfilled') {
  155. this.fulfill(this._result);
  156. } else if (this._status === 'rejected') {
  157. this.reject(this._result);
  158. }
  159.  
  160. return then;
  161. },
  162.  
  163. /**
  164. Wraps the callback in Y.soon to guarantee its asynchronous execution. It
  165. also catches exceptions to turn them into rejections and links promises
  166. returned from the `then` callback.
  167.  
  168. @method _wrap
  169. @param {Function} thenFulfill Fulfillment function of the resolver that
  170. handles this promise
  171. @param {Function} thenReject Rejection function of the resolver that
  172. handles this promise
  173. @param {Function} fn Callback to wrap
  174. @return {Function}
  175. @private
  176. **/
  177. _wrap: function (thenFulfill, thenReject, fn) {
  178. var promise = this.promise;
  179.  
  180. return function () {
  181. // The args coming in to the callback/errback from the
  182. // resolution of the parent promise.
  183. var args = arguments;
  184.  
  185. // Wrapping all callbacks in Y.soon to guarantee
  186. // asynchronicity. Because setTimeout can cause unnecessary
  187. // delays that *can* become noticeable in some situations
  188. // (especially in Node.js)
  189. Y.soon(function () {
  190. // Call the callback/errback with promise as `this` to
  191. // preserve the contract that access to the deferred is
  192. // only for code that may resolve/reject it.
  193. // Another option would be call the function from the
  194. // global context, but it seemed less useful.
  195. var result;
  196.  
  197. // Promises model exception handling through callbacks
  198. // making both synchronous and asynchronous errors behave
  199. // the same way
  200. try {
  201. result = fn.apply(promise, args);
  202. } catch (e) {
  203. return thenReject(e);
  204. }
  205.  
  206. if (Promise.isPromise(result)) {
  207. // Returning a promise from a callback makes the current
  208. // promise sync up with the returned promise
  209. result.then(thenFulfill, thenReject);
  210. } else {
  211. // Non-promise return values always trigger resolve()
  212. // because callback is affirmative, and errback is
  213. // recovery. To continue on the rejection path, errbacks
  214. // must return rejected promises or throw.
  215. thenFulfill(result);
  216. }
  217. });
  218. };
  219. },
  220.  
  221. /**
  222. Returns the current status of the Resolver as a string "pending",
  223. "fulfilled", or "rejected".
  224.  
  225. @method getStatus
  226. @return {String}
  227. **/
  228. getStatus: function () {
  229. return this._status;
  230. },
  231.  
  232. /**
  233. Executes an array of callbacks from a specified context, passing a set of
  234. arguments.
  235.  
  236. @method _notify
  237. @param {Function[]} subs The array of subscriber callbacks
  238. @param {Any} result Value to pass the callbacks
  239. @protected
  240. **/
  241. _notify: function (subs, result) {
  242. var i, len;
  243.  
  244. for (i = 0, len = subs.length; i < len; ++i) {
  245. subs[i](result);
  246. }
  247. }
  248.  
  249. }, true);
  250.  
  251. Y.Promise.Resolver = Resolver;
  252.