promise.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. define(function(require, exports, module) {
  2. /*!
  3. ** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
  4. ** Copyright (c) 2013-2014 Ralf S. Engelschall <http://engelschall.com>
  5. ** Licensed under The MIT License <http://opensource.org/licenses/MIT>
  6. ** Source-Code distributed on <http://github.com/rse/thenable>
  7. */
  8. /* promise states [Promises/A+ 2.1] */
  9. var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
  10. var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
  11. var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
  12. /* promise object constructor */
  13. var Promise = function(executor) {
  14. /* optionally support non-constructor/plain-function call */
  15. if (!(this instanceof Promise))
  16. return new Promise(executor);
  17. /* initialize object */
  18. this.id = 'Thenable/1.0.7';
  19. this.state = STATE_PENDING; /* initial state */
  20. this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
  21. this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
  22. this.onFulfilled = []; /* initial handlers */
  23. this.onRejected = []; /* initial handlers */
  24. /* support optional executor function */
  25. if (typeof executor === 'function')
  26. executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
  27. };
  28. /* Promise API methods */
  29. Promise.prototype = {
  30. /* promise resolving methods */
  31. fulfill: function(value) { return deliver(this, STATE_FULFILLED, 'fulfillValue', value); },
  32. reject: function(value) { return deliver(this, STATE_REJECTED, 'rejectReason', value); },
  33. /* 'The then Method' [Promises/A+ 1.1, 1.2, 2.2] */
  34. then: function(onFulfilled, onRejected) {
  35. var curr = this;
  36. var next = new Promise(); /* [Promises/A+ 2.2.7] */
  37. curr.onFulfilled.push(
  38. resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
  39. curr.onRejected.push(
  40. resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
  41. execute(curr);
  42. return next; /* [Promises/A+ 2.2.7, 3.3] */
  43. }
  44. };
  45. Promise.all = function (arr) {
  46. return new Promise(function(resolve, reject) {
  47. var len = arr.length,
  48. i = 0,
  49. res = 0,
  50. results = [];
  51. if (len === 0) {
  52. resolve(results);
  53. }
  54. while (i < len) {
  55. arr[i].then(
  56. function (result) {
  57. results.push(result);
  58. if (++res === len) {
  59. resolve(results);
  60. }
  61. },
  62. function (val) {
  63. reject(val);
  64. }
  65. );
  66. i++;
  67. }
  68. });
  69. };
  70. /* deliver an action */
  71. var deliver = function(curr, state, name, value) {
  72. if (curr.state === STATE_PENDING) {
  73. curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
  74. curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
  75. execute(curr);
  76. }
  77. return curr;
  78. };
  79. /* execute all handlers */
  80. var execute = function(curr) {
  81. if (curr.state === STATE_FULFILLED)
  82. execute_handlers(curr, 'onFulfilled', curr.fulfillValue);
  83. else if (curr.state === STATE_REJECTED)
  84. execute_handlers(curr, 'onRejected', curr.rejectReason);
  85. };
  86. /* execute particular set of handlers */
  87. var execute_handlers = function(curr, name, value) {
  88. /* global process: true */
  89. /* global setImmediate: true */
  90. /* global setTimeout: true */
  91. /* short-circuit processing */
  92. if (curr[name].length === 0)
  93. return;
  94. /* iterate over all handlers, exactly once */
  95. var handlers = curr[name];
  96. curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
  97. var func = function() {
  98. for (var i = 0; i < handlers.length; i++)
  99. handlers[i](value); /* [Promises/A+ 2.2.5] */
  100. };
  101. /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
  102. if (typeof process === 'object' && typeof process.nextTick === 'function')
  103. process.nextTick(func);
  104. else if (typeof setImmediate === 'function')
  105. setImmediate(func);
  106. else
  107. setTimeout(func, 0);
  108. };
  109. /* generate a resolver function */
  110. var resolver = function(cb, next, method) {
  111. return function(value) {
  112. if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
  113. next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
  114. else {
  115. var result;
  116. try {
  117. if (value instanceof Promise) {
  118. result = value.then(cb);
  119. }
  120. else result = cb(value);
  121. } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
  122. catch (e) {
  123. next.reject(e); /* [Promises/A+ 2.2.7.2] */
  124. return;
  125. }
  126. resolve(next, result); /* [Promises/A+ 2.2.7.1] */
  127. }
  128. };
  129. };
  130. /* 'Promise Resolution Procedure' */ /* [Promises/A+ 2.3] */
  131. var resolve = function(promise, x) {
  132. /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
  133. if (promise === x) {
  134. promise.reject(new TypeError('cannot resolve promise with itself'));
  135. return;
  136. }
  137. /* surgically check for a 'then' method
  138. (mainly to just call the 'getter' of 'then' only once) */
  139. var then;
  140. if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
  141. try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */
  142. catch (e) {
  143. promise.reject(e); /* [Promises/A+ 2.3.3.2] */
  144. return;
  145. }
  146. }
  147. /* handle own Thenables [Promises/A+ 2.3.2]
  148. and similar 'thenables' [Promises/A+ 2.3.3] */
  149. if (typeof then === 'function') {
  150. var resolved = false;
  151. try {
  152. /* call retrieved 'then' method */ /* [Promises/A+ 2.3.3.3] */
  153. then.call(x,
  154. /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
  155. function(y) {
  156. if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
  157. if (y === x) /* [Promises/A+ 3.6] */
  158. promise.reject(new TypeError('circular thenable chain'));
  159. else
  160. resolve(promise, y);
  161. },
  162. /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
  163. function(r) {
  164. if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
  165. promise.reject(r);
  166. }
  167. );
  168. }
  169. catch (e) {
  170. if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
  171. promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
  172. }
  173. return;
  174. }
  175. /* handle other values */
  176. promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
  177. };
  178. Promise.resolve = function(value) {
  179. return new Promise(function(resolve) {
  180. resolve(value);
  181. });
  182. };
  183. Promise.reject = function(reason) {
  184. return new Promise(function(resolve, reject) {
  185. reject(reason);
  186. });
  187. };
  188. /* export API */
  189. module.exports = Promise;
  190. });