Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

281 řádky
6.6 KiB

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