promise.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // https://github.com/taylorhakes/promise-polyfill
  2. (function (global, factory) {
  3. typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
  4. typeof define === 'function' && define.amd ? define(factory) :
  5. (factory());
  6. }(this, (function () { 'use strict';
  7. var promiseFinally = function(callback) {
  8. var constructor = this.constructor;
  9. return this.then(
  10. function(value) {
  11. return constructor.resolve(callback()).then(function() {
  12. return value;
  13. });
  14. },
  15. function(reason) {
  16. return constructor.resolve(callback()).then(function() {
  17. return constructor.reject(reason);
  18. });
  19. }
  20. );
  21. };
  22. // Store setTimeout reference so promise-polyfill will be unaffected by
  23. // other code modifying setTimeout (like sinon.useFakeTimers())
  24. var setTimeoutFunc = setTimeout;
  25. function noop() {}
  26. // Polyfill for Function.prototype.bind
  27. function bind(fn, thisArg) {
  28. return function() {
  29. fn.apply(thisArg, arguments);
  30. };
  31. }
  32. function Promise(fn) {
  33. if (!(this instanceof Promise))
  34. throw new TypeError('Promises must be constructed via new');
  35. if (typeof fn !== 'function') throw new TypeError('not a function');
  36. this._state = 0;
  37. this._handled = false;
  38. this._value = undefined;
  39. this._deferreds = [];
  40. doResolve(fn, this);
  41. }
  42. function handle(self, deferred) {
  43. while (self._state === 3) {
  44. self = self._value;
  45. }
  46. if (self._state === 0) {
  47. self._deferreds.push(deferred);
  48. return;
  49. }
  50. self._handled = true;
  51. Promise._immediateFn(function() {
  52. var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
  53. if (cb === null) {
  54. (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
  55. return;
  56. }
  57. var ret;
  58. try {
  59. ret = cb(self._value);
  60. } catch (e) {
  61. reject(deferred.promise, e);
  62. return;
  63. }
  64. resolve(deferred.promise, ret);
  65. });
  66. }
  67. function resolve(self, newValue) {
  68. try {
  69. // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  70. if (newValue === self)
  71. throw new TypeError('A promise cannot be resolved with itself.');
  72. if (
  73. newValue &&
  74. (typeof newValue === 'object' || typeof newValue === 'function')
  75. ) {
  76. var then = newValue.then;
  77. if (newValue instanceof Promise) {
  78. self._state = 3;
  79. self._value = newValue;
  80. finale(self);
  81. return;
  82. } else if (typeof then === 'function') {
  83. doResolve(bind(then, newValue), self);
  84. return;
  85. }
  86. }
  87. self._state = 1;
  88. self._value = newValue;
  89. finale(self);
  90. } catch (e) {
  91. reject(self, e);
  92. }
  93. }
  94. function reject(self, newValue) {
  95. self._state = 2;
  96. self._value = newValue;
  97. finale(self);
  98. }
  99. function finale(self) {
  100. if (self._state === 2 && self._deferreds.length === 0) {
  101. Promise._immediateFn(function() {
  102. if (!self._handled) {
  103. Promise._unhandledRejectionFn(self._value);
  104. }
  105. });
  106. }
  107. for (var i = 0, len = self._deferreds.length; i < len; i++) {
  108. handle(self, self._deferreds[i]);
  109. }
  110. self._deferreds = null;
  111. }
  112. function Handler(onFulfilled, onRejected, promise) {
  113. this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  114. this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  115. this.promise = promise;
  116. }
  117. /**
  118. * Take a potentially misbehaving resolver function and make sure
  119. * onFulfilled and onRejected are only called once.
  120. *
  121. * Makes no guarantees about asynchrony.
  122. */
  123. function doResolve(fn, self) {
  124. var done = false;
  125. try {
  126. fn(
  127. function(value) {
  128. if (done) return;
  129. done = true;
  130. resolve(self, value);
  131. },
  132. function(reason) {
  133. if (done) return;
  134. done = true;
  135. reject(self, reason);
  136. }
  137. );
  138. } catch (ex) {
  139. if (done) return;
  140. done = true;
  141. reject(self, ex);
  142. }
  143. }
  144. Promise.prototype['catch'] = function(onRejected) {
  145. return this.then(null, onRejected);
  146. };
  147. Promise.prototype.then = function(onFulfilled, onRejected) {
  148. var prom = new this.constructor(noop);
  149. handle(this, new Handler(onFulfilled, onRejected, prom));
  150. return prom;
  151. };
  152. Promise.prototype['finally'] = promiseFinally;
  153. Promise.all = function(arr) {
  154. return new Promise(function(resolve, reject) {
  155. if (!arr || typeof arr.length === 'undefined')
  156. throw new TypeError('Promise.all accepts an array');
  157. var args = Array.prototype.slice.call(arr);
  158. if (args.length === 0) return resolve([]);
  159. var remaining = args.length;
  160. function res(i, val) {
  161. try {
  162. if (val && (typeof val === 'object' || typeof val === 'function')) {
  163. var then = val.then;
  164. if (typeof then === 'function') {
  165. then.call(
  166. val,
  167. function(val) {
  168. res(i, val);
  169. },
  170. reject
  171. );
  172. return;
  173. }
  174. }
  175. args[i] = val;
  176. if (--remaining === 0) {
  177. resolve(args);
  178. }
  179. } catch (ex) {
  180. reject(ex);
  181. }
  182. }
  183. for (var i = 0; i < args.length; i++) {
  184. res(i, args[i]);
  185. }
  186. });
  187. };
  188. Promise.resolve = function(value) {
  189. if (value && typeof value === 'object' && value.constructor === Promise) {
  190. return value;
  191. }
  192. return new Promise(function(resolve) {
  193. resolve(value);
  194. });
  195. };
  196. Promise.reject = function(value) {
  197. return new Promise(function(resolve, reject) {
  198. reject(value);
  199. });
  200. };
  201. Promise.race = function(values) {
  202. return new Promise(function(resolve, reject) {
  203. for (var i = 0, len = values.length; i < len; i++) {
  204. values[i].then(resolve, reject);
  205. }
  206. });
  207. };
  208. // Use polyfill for setImmediate for performance gains
  209. Promise._immediateFn =
  210. (typeof setImmediate === 'function' &&
  211. function(fn) {
  212. setImmediate(fn);
  213. }) ||
  214. function(fn) {
  215. setTimeoutFunc(fn, 0);
  216. };
  217. Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
  218. if (typeof console !== 'undefined' && console) {
  219. console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
  220. }
  221. };
  222. var globalNS = (function() {
  223. // the only reliable means to get the global object is
  224. // `Function('return this')()`
  225. // However, this causes CSP violations in Chrome apps.
  226. if (typeof self !== 'undefined') {
  227. return self;
  228. }
  229. if (typeof window !== 'undefined') {
  230. return window;
  231. }
  232. if (typeof global !== 'undefined') {
  233. return global;
  234. }
  235. throw new Error('unable to locate global object');
  236. })();
  237. if (!globalNS.Promise) {
  238. globalNS.Promise = Promise;
  239. } else if (!globalNS.Promise.prototype['finally']) {
  240. globalNS.Promise.prototype['finally'] = promiseFinally;
  241. }
  242. })));