promise.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. ;(function(id, factory) { // Module boilerplate :(
  2. if (typeof(define) === 'function') { // RequireJS
  3. define(factory);
  4. } else if (typeof(require) === 'function') { // CommonJS
  5. factory.call(this, require, exports, module);
  6. } else if (String(this).indexOf('BackstagePass') >= 0) { // JSM
  7. this[factory.name] = {};
  8. try {
  9. this.console = this['Components'].utils
  10. .import('resource://gre/modules/devtools/Console.jsm', {}).console;
  11. }
  12. catch (ex) {
  13. // Avoid failures on different toolkit configurations.
  14. }
  15. factory(function require(uri) {
  16. var imports = {};
  17. this['Components'].utils.import(uri, imports);
  18. return imports;
  19. }, this[factory.name], { uri: __URI__, id: id });
  20. this.EXPORTED_SYMBOLS = [factory.name];
  21. } else if (~String(this).indexOf('Sandbox')) { // Sandbox
  22. factory(function require(uri) {}, this, { id: id });
  23. } else { // Browser or alike
  24. var globals = this;
  25. factory(function require(id) {
  26. return globals[id];
  27. }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
  28. }
  29. }).call(this, 'promise/core', function Promise(require, exports, module) {
  30. 'use strict';
  31. module.metadata = {
  32. "stability": "unstable"
  33. };
  34. /**
  35. * Internal utility: Wraps given `value` into simplified promise, successfully
  36. * fulfilled to a given `value`. Note the result is not a complete promise
  37. * implementation, as its method `then` does not returns anything.
  38. */
  39. function fulfilled(value) {
  40. return { then: function then(fulfill) { fulfill(value); } };
  41. }
  42. /**
  43. * Internal utility: Wraps given input into simplified promise, pre-rejected
  44. * with a given `reason`. Note the result is not a complete promise
  45. * implementation, as its method `then` does not returns anything.
  46. */
  47. function rejected(reason) {
  48. return { then: function then(fulfill, reject) { reject(reason); } };
  49. }
  50. /**
  51. * Internal utility: Returns `true` if given `value` is a promise. Value is
  52. * assumed to be a promise if it implements method `then`.
  53. */
  54. function isPromise(value) {
  55. return value && typeof(value.then) === 'function';
  56. }
  57. /**
  58. * Creates deferred object containing fresh promise & methods to either resolve
  59. * or reject it. The result is an object with the following properties:
  60. * - `promise` Eventual value representation implementing CommonJS [Promises/A]
  61. * (http://wiki.commonjs.org/wiki/Promises/A) API.
  62. * - `resolve` Single shot function that resolves enclosed `promise` with a
  63. * given `value`.
  64. * - `reject` Single shot function that rejects enclosed `promise` with a given
  65. * `reason`.
  66. *
  67. * An optional `prototype` argument is used as a prototype of the returned
  68. * `promise` allowing one to implement additional API. If prototype is not
  69. * passed then it falls back to `Object.prototype`.
  70. *
  71. * ## Example
  72. *
  73. * function fetchURI(uri, type) {
  74. * var deferred = defer();
  75. * var request = new XMLHttpRequest();
  76. * request.open("GET", uri, true);
  77. * request.responseType = type;
  78. * request.onload = function onload() {
  79. * deferred.resolve(request.response);
  80. * }
  81. * request.onerror = function(event) {
  82. * deferred.reject(event);
  83. * }
  84. * request.send();
  85. *
  86. * return deferred.promise;
  87. * }
  88. */
  89. function defer(prototype) {
  90. // Define FIFO queue of observer pairs. Once promise is resolved & all queued
  91. // observers are forwarded to `result` and variable is set to `null`.
  92. var observers = [];
  93. // Promise `result`, which will be assigned a resolution value once promise
  94. // is resolved. Note that result will always be assigned promise (or alike)
  95. // object to take care of propagation through promise chains. If result is
  96. // `null` promise is not resolved yet.
  97. var result = null;
  98. prototype = (prototype || prototype === null) ? prototype : Object.prototype;
  99. // Create an object implementing promise API.
  100. var promise = Object.create(prototype, {
  101. then: { value: function then(onFulfill, onError) {
  102. var deferred = defer(prototype);
  103. function resolve(value) {
  104. // If `onFulfill` handler is provided resolve `deferred.promise` with
  105. // result of invoking it with a resolution value. If handler is not
  106. // provided propagate value through.
  107. try {
  108. deferred.resolve(onFulfill ? onFulfill(value) : value);
  109. }
  110. // `onFulfill` may throw exception in which case resulting promise
  111. // is rejected with thrown exception.
  112. catch(error) {
  113. if (exports._reportErrors && typeof(console) === 'object')
  114. console.error(error);
  115. // Note: Following is equivalent of `deferred.reject(error)`,
  116. // we use this shortcut to reduce a stack.
  117. deferred.resolve(rejected(error));
  118. }
  119. }
  120. function reject(reason) {
  121. try {
  122. if (onError) deferred.resolve(onError(reason));
  123. else deferred.resolve(rejected(reason));
  124. }
  125. catch(error) {
  126. if (exports._reportErrors && typeof(console) === 'object')
  127. console.error(error);
  128. deferred.resolve(rejected(error));
  129. }
  130. }
  131. // If enclosed promise (`this.promise`) observers queue is still alive
  132. // enqueue a new observer pair into it. Note that this does not
  133. // necessary means that promise is pending, it may already be resolved,
  134. // but we still have to queue observers to guarantee an order of
  135. // propagation.
  136. if (observers) {
  137. observers.push({ resolve: resolve, reject: reject });
  138. }
  139. // Otherwise just forward observer pair right to a `result` promise.
  140. else {
  141. result.then(resolve, reject);
  142. }
  143. return deferred.promise;
  144. }}
  145. })
  146. var deferred = {
  147. promise: promise,
  148. /**
  149. * Resolves associated `promise` to a given `value`, unless it's already
  150. * resolved or rejected. Note that resolved promise is not necessary a
  151. * successfully fulfilled. Promise may be resolved with a promise `value`
  152. * in which case `value` promise's fulfillment / rejection will propagate
  153. * up to a promise resolved with `value`.
  154. */
  155. resolve: function resolve(value) {
  156. if (!result) {
  157. // Store resolution `value` in a `result` as a promise, so that all
  158. // the subsequent handlers can be simply forwarded to it. Since
  159. // `result` will be a promise all the value / error propagation will
  160. // be uniformly taken care of.
  161. result = isPromise(value) ? value : fulfilled(value);
  162. // Forward already registered observers to a `result` promise in the
  163. // order they were registered. Note that we intentionally dequeue
  164. // observer at a time until queue is exhausted. This makes sure that
  165. // handlers registered as side effect of observer forwarding are
  166. // queued instead of being invoked immediately, guaranteeing FIFO
  167. // order.
  168. while (observers.length) {
  169. var observer = observers.shift();
  170. result.then(observer.resolve, observer.reject);
  171. }
  172. // Once `observers` queue is exhausted we `null`-ify it, so that
  173. // new handlers are forwarded straight to the `result`.
  174. observers = null;
  175. }
  176. },
  177. /**
  178. * Rejects associated `promise` with a given `reason`, unless it's already
  179. * resolved / rejected. This is just a (better performing) convenience
  180. * shortcut for `deferred.resolve(reject(reason))`.
  181. */
  182. reject: function reject(reason) {
  183. // Note that if promise is resolved that does not necessary means that it
  184. // is successfully fulfilled. Resolution value may be a promise in which
  185. // case its result propagates. In other words if promise `a` is resolved
  186. // with promise `b`, `a` is either fulfilled or rejected depending
  187. // on weather `b` is fulfilled or rejected. Here `deferred.promise` is
  188. // resolved with a promise pre-rejected with a given `reason`, there for
  189. // `deferred.promise` is rejected with a given `reason`. This may feel
  190. // little awkward first, but doing it this way greatly simplifies
  191. // propagation through promise chains.
  192. deferred.resolve(rejected(reason));
  193. }
  194. };
  195. return deferred;
  196. }
  197. exports.defer = defer;
  198. /**
  199. * Returns a promise resolved to a given `value`. Optionally a second
  200. * `prototype` argument may be provided to be used as a prototype for the
  201. * returned promise.
  202. */
  203. function resolve(value, prototype) {
  204. var deferred = defer(prototype);
  205. deferred.resolve(value);
  206. return deferred.promise;
  207. }
  208. exports.resolve = resolve;
  209. /**
  210. * Returns a promise rejected with a given `reason`. Optionally a second
  211. * `prototype` argument may be provided to be used as a prototype for the
  212. * returned promise.
  213. */
  214. function reject(reason, prototype) {
  215. var deferred = defer(prototype);
  216. deferred.reject(reason);
  217. return deferred.promise;
  218. }
  219. exports.reject = reject;
  220. var promised = (function() {
  221. // Note: Define shortcuts and utility functions here in order to avoid
  222. // slower property accesses and unnecessary closure creations on each
  223. // call of this popular function.
  224. var call = Function.call;
  225. var concat = Array.prototype.concat;
  226. // Utility function that does following:
  227. // execute([ f, self, args...]) => f.apply(self, args)
  228. function execute(args) { return call.apply(call, args) }
  229. // Utility function that takes promise of `a` array and maybe promise `b`
  230. // as arguments and returns promise for `a.concat(b)`.
  231. function promisedConcat(promises, unknown) {
  232. return promises.then(function(values) {
  233. return resolve(unknown).then(function(value) {
  234. return values.concat([ value ]);
  235. });
  236. });
  237. }
  238. return function promised(f, prototype) {
  239. /**
  240. Returns a wrapped `f`, which when called returns a promise that resolves to
  241. `f(...)` passing all the given arguments to it, which by the way may be
  242. promises. Optionally second `prototype` argument may be provided to be used
  243. a prototype for a returned promise.
  244. ## Example
  245. var promise = promised(Array)(1, promise(2), promise(3))
  246. promise.then(console.log) // => [ 1, 2, 3 ]
  247. **/
  248. return function promised() {
  249. // create array of [ f, this, args... ]
  250. return concat.apply([ f, this ], arguments).
  251. // reduce it via `promisedConcat` to get promised array of fulfillments
  252. reduce(promisedConcat, resolve([], prototype)).
  253. // finally map that to promise of `f.apply(this, args...)`
  254. then(execute);
  255. };
  256. }
  257. })();
  258. exports.promised = promised;
  259. var all = promised(Array);
  260. exports.all = all;
  261. });