You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
5.8 KiB

4 years ago
  1. 'use strict';
  2. var PENDING = 'pending';
  3. var SETTLED = 'settled';
  4. var FULFILLED = 'fulfilled';
  5. var REJECTED = 'rejected';
  6. var NOOP = function () {};
  7. var isNode = typeof global !== 'undefined' && typeof global.process !== 'undefined' && typeof global.process.emit === 'function';
  8. var asyncSetTimer = typeof setImmediate === 'undefined' ? setTimeout : setImmediate;
  9. var asyncQueue = [];
  10. var asyncTimer;
  11. function asyncFlush() {
  12. // run promise callbacks
  13. for (var i = 0; i < asyncQueue.length; i++) {
  14. asyncQueue[i][0](asyncQueue[i][1]);
  15. }
  16. // reset async asyncQueue
  17. asyncQueue = [];
  18. asyncTimer = false;
  19. }
  20. function asyncCall(callback, arg) {
  21. asyncQueue.push([callback, arg]);
  22. if (!asyncTimer) {
  23. asyncTimer = true;
  24. asyncSetTimer(asyncFlush, 0);
  25. }
  26. }
  27. function invokeResolver(resolver, promise) {
  28. function resolvePromise(value) {
  29. resolve(promise, value);
  30. }
  31. function rejectPromise(reason) {
  32. reject(promise, reason);
  33. }
  34. try {
  35. resolver(resolvePromise, rejectPromise);
  36. } catch (e) {
  37. rejectPromise(e);
  38. }
  39. }
  40. function invokeCallback(subscriber) {
  41. var owner = subscriber.owner;
  42. var settled = owner._state;
  43. var value = owner._data;
  44. var callback = subscriber[settled];
  45. var promise = subscriber.then;
  46. if (typeof callback === 'function') {
  47. settled = FULFILLED;
  48. try {
  49. value = callback(value);
  50. } catch (e) {
  51. reject(promise, e);
  52. }
  53. }
  54. if (!handleThenable(promise, value)) {
  55. if (settled === FULFILLED) {
  56. resolve(promise, value);
  57. }
  58. if (settled === REJECTED) {
  59. reject(promise, value);
  60. }
  61. }
  62. }
  63. function handleThenable(promise, value) {
  64. var resolved;
  65. try {
  66. if (promise === value) {
  67. throw new TypeError('A promises callback cannot return that same promise.');
  68. }
  69. if (value && (typeof value === 'function' || typeof value === 'object')) {
  70. // then should be retrieved only once
  71. var then = value.then;
  72. if (typeof then === 'function') {
  73. then.call(value, function (val) {
  74. if (!resolved) {
  75. resolved = true;
  76. if (value === val) {
  77. fulfill(promise, val);
  78. } else {
  79. resolve(promise, val);
  80. }
  81. }
  82. }, function (reason) {
  83. if (!resolved) {
  84. resolved = true;
  85. reject(promise, reason);
  86. }
  87. });
  88. return true;
  89. }
  90. }
  91. } catch (e) {
  92. if (!resolved) {
  93. reject(promise, e);
  94. }
  95. return true;
  96. }
  97. return false;
  98. }
  99. function resolve(promise, value) {
  100. if (promise === value || !handleThenable(promise, value)) {
  101. fulfill(promise, value);
  102. }
  103. }
  104. function fulfill(promise, value) {
  105. if (promise._state === PENDING) {
  106. promise._state = SETTLED;
  107. promise._data = value;
  108. asyncCall(publishFulfillment, promise);
  109. }
  110. }
  111. function reject(promise, reason) {
  112. if (promise._state === PENDING) {
  113. promise._state = SETTLED;
  114. promise._data = reason;
  115. asyncCall(publishRejection, promise);
  116. }
  117. }
  118. function publish(promise) {
  119. promise._then = promise._then.forEach(invokeCallback);
  120. }
  121. function publishFulfillment(promise) {
  122. promise._state = FULFILLED;
  123. publish(promise);
  124. }
  125. function publishRejection(promise) {
  126. promise._state = REJECTED;
  127. publish(promise);
  128. if (!promise._handled && isNode) {
  129. global.process.emit('unhandledRejection', promise._data, promise);
  130. }
  131. }
  132. function notifyRejectionHandled(promise) {
  133. global.process.emit('rejectionHandled', promise);
  134. }
  135. /**
  136. * @class
  137. */
  138. function Promise(resolver) {
  139. if (typeof resolver !== 'function') {
  140. throw new TypeError('Promise resolver ' + resolver + ' is not a function');
  141. }
  142. if (this instanceof Promise === false) {
  143. throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');
  144. }
  145. this._then = [];
  146. invokeResolver(resolver, this);
  147. }
  148. Promise.prototype = {
  149. constructor: Promise,
  150. _state: PENDING,
  151. _then: null,
  152. _data: undefined,
  153. _handled: false,
  154. then: function (onFulfillment, onRejection) {
  155. var subscriber = {
  156. owner: this,
  157. then: new this.constructor(NOOP),
  158. fulfilled: onFulfillment,
  159. rejected: onRejection
  160. };
  161. if ((onRejection || onFulfillment) && !this._handled) {
  162. this._handled = true;
  163. if (this._state === REJECTED && isNode) {
  164. asyncCall(notifyRejectionHandled, this);
  165. }
  166. }
  167. if (this._state === FULFILLED || this._state === REJECTED) {
  168. // already resolved, call callback async
  169. asyncCall(invokeCallback, subscriber);
  170. } else {
  171. // subscribe
  172. this._then.push(subscriber);
  173. }
  174. return subscriber.then;
  175. },
  176. catch: function (onRejection) {
  177. return this.then(null, onRejection);
  178. }
  179. };
  180. Promise.all = function (promises) {
  181. if (!Array.isArray(promises)) {
  182. throw new TypeError('You must pass an array to Promise.all().');
  183. }
  184. return new Promise(function (resolve, reject) {
  185. var results = [];
  186. var remaining = 0;
  187. function resolver(index) {
  188. remaining++;
  189. return function (value) {
  190. results[index] = value;
  191. if (!--remaining) {
  192. resolve(results);
  193. }
  194. };
  195. }
  196. for (var i = 0, promise; i < promises.length; i++) {
  197. promise = promises[i];
  198. if (promise && typeof promise.then === 'function') {
  199. promise.then(resolver(i), reject);
  200. } else {
  201. results[i] = promise;
  202. }
  203. }
  204. if (!remaining) {
  205. resolve(results);
  206. }
  207. });
  208. };
  209. Promise.race = function (promises) {
  210. if (!Array.isArray(promises)) {
  211. throw new TypeError('You must pass an array to Promise.race().');
  212. }
  213. return new Promise(function (resolve, reject) {
  214. for (var i = 0, promise; i < promises.length; i++) {
  215. promise = promises[i];
  216. if (promise && typeof promise.then === 'function') {
  217. promise.then(resolve, reject);
  218. } else {
  219. resolve(promise);
  220. }
  221. }
  222. });
  223. };
  224. Promise.resolve = function (value) {
  225. if (value && typeof value === 'object' && value.constructor === Promise) {
  226. return value;
  227. }
  228. return new Promise(function (resolve) {
  229. resolve(value);
  230. });
  231. };
  232. Promise.reject = function (reason) {
  233. return new Promise(function (resolve, reject) {
  234. reject(reason);
  235. });
  236. };
  237. module.exports = Promise;