polyfill.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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. // @ts-ignore
  10. return constructor.resolve(callback()).then(function() {
  11. return value;
  12. });
  13. },
  14. function(reason) {
  15. // @ts-ignore
  16. return constructor.resolve(callback()).then(function() {
  17. // @ts-ignore
  18. return constructor.reject(reason);
  19. });
  20. }
  21. );
  22. }
  23. function allSettled(arr) {
  24. var P = this;
  25. return new P(function(resolve, reject) {
  26. if (!(arr && typeof arr.length !== 'undefined')) {
  27. return reject(
  28. new TypeError(
  29. typeof arr +
  30. ' ' +
  31. arr +
  32. ' is not iterable(cannot read property Symbol(Symbol.iterator))'
  33. )
  34. );
  35. }
  36. var args = Array.prototype.slice.call(arr);
  37. if (args.length === 0) return resolve([]);
  38. var remaining = args.length;
  39. function res(i, val) {
  40. if (val && (typeof val === 'object' || typeof val === 'function')) {
  41. var then = val.then;
  42. if (typeof then === 'function') {
  43. then.call(
  44. val,
  45. function(val) {
  46. res(i, val);
  47. },
  48. function(e) {
  49. args[i] = { status: 'rejected', reason: e };
  50. if (--remaining === 0) {
  51. resolve(args);
  52. }
  53. }
  54. );
  55. return;
  56. }
  57. }
  58. args[i] = { status: 'fulfilled', value: val };
  59. if (--remaining === 0) {
  60. resolve(args);
  61. }
  62. }
  63. for (var i = 0; i < args.length; i++) {
  64. res(i, args[i]);
  65. }
  66. });
  67. }
  68. /**
  69. * @constructor
  70. */
  71. function AggregateError(errors, message) {
  72. this.name = 'AggregateError', this.errors = errors;
  73. this.message = message || '';
  74. }
  75. AggregateError.prototype = Error.prototype;
  76. function any(arr) {
  77. var P = this;
  78. return new P(function(resolve, reject) {
  79. if (!(arr && typeof arr.length !== 'undefined')) {
  80. return reject(new TypeError('Promise.any accepts an array'));
  81. }
  82. var args = Array.prototype.slice.call(arr);
  83. if (args.length === 0) return reject();
  84. var rejectionReasons = [];
  85. for (var i = 0; i < args.length; i++) {
  86. try {
  87. P.resolve(args[i])
  88. .then(resolve)
  89. .catch(function(error) {
  90. rejectionReasons.push(error);
  91. if (rejectionReasons.length === args.length) {
  92. reject(
  93. new AggregateError(
  94. rejectionReasons,
  95. 'All promises were rejected'
  96. )
  97. );
  98. }
  99. });
  100. } catch (ex) {
  101. reject(ex);
  102. }
  103. }
  104. });
  105. }
  106. // Store setTimeout reference so promise-polyfill will be unaffected by
  107. // other code modifying setTimeout (like sinon.useFakeTimers())
  108. var setTimeoutFunc = setTimeout;
  109. function isArray(x) {
  110. return Boolean(x && typeof x.length !== 'undefined');
  111. }
  112. function noop() {}
  113. // Polyfill for Function.prototype.bind
  114. function bind(fn, thisArg) {
  115. return function() {
  116. fn.apply(thisArg, arguments);
  117. };
  118. }
  119. /**
  120. * @constructor
  121. * @param {Function} fn
  122. */
  123. function Promise(fn) {
  124. if (!(this instanceof Promise))
  125. throw new TypeError('Promises must be constructed via new');
  126. if (typeof fn !== 'function') throw new TypeError('not a function');
  127. /** @type {!number} */
  128. this._state = 0;
  129. /** @type {!boolean} */
  130. this._handled = false;
  131. /** @type {Promise|undefined} */
  132. this._value = undefined;
  133. /** @type {!Array<!Function>} */
  134. this._deferreds = [];
  135. doResolve(fn, this);
  136. }
  137. function handle(self, deferred) {
  138. while (self._state === 3) {
  139. self = self._value;
  140. }
  141. if (self._state === 0) {
  142. self._deferreds.push(deferred);
  143. return;
  144. }
  145. self._handled = true;
  146. Promise._immediateFn(function() {
  147. var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
  148. if (cb === null) {
  149. (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
  150. return;
  151. }
  152. var ret;
  153. try {
  154. ret = cb(self._value);
  155. } catch (e) {
  156. reject(deferred.promise, e);
  157. return;
  158. }
  159. resolve(deferred.promise, ret);
  160. });
  161. }
  162. function resolve(self, newValue) {
  163. try {
  164. // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  165. if (newValue === self)
  166. throw new TypeError('A promise cannot be resolved with itself.');
  167. if (
  168. newValue &&
  169. (typeof newValue === 'object' || typeof newValue === 'function')
  170. ) {
  171. var then = newValue.then;
  172. if (newValue instanceof Promise) {
  173. self._state = 3;
  174. self._value = newValue;
  175. finale(self);
  176. return;
  177. } else if (typeof then === 'function') {
  178. doResolve(bind(then, newValue), self);
  179. return;
  180. }
  181. }
  182. self._state = 1;
  183. self._value = newValue;
  184. finale(self);
  185. } catch (e) {
  186. reject(self, e);
  187. }
  188. }
  189. function reject(self, newValue) {
  190. self._state = 2;
  191. self._value = newValue;
  192. finale(self);
  193. }
  194. function finale(self) {
  195. if (self._state === 2 && self._deferreds.length === 0) {
  196. Promise._immediateFn(function() {
  197. if (!self._handled) {
  198. Promise._unhandledRejectionFn(self._value);
  199. }
  200. });
  201. }
  202. for (var i = 0, len = self._deferreds.length; i < len; i++) {
  203. handle(self, self._deferreds[i]);
  204. }
  205. self._deferreds = null;
  206. }
  207. /**
  208. * @constructor
  209. */
  210. function Handler(onFulfilled, onRejected, promise) {
  211. this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  212. this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  213. this.promise = promise;
  214. }
  215. /**
  216. * Take a potentially misbehaving resolver function and make sure
  217. * onFulfilled and onRejected are only called once.
  218. *
  219. * Makes no guarantees about asynchrony.
  220. */
  221. function doResolve(fn, self) {
  222. var done = false;
  223. try {
  224. fn(
  225. function(value) {
  226. if (done) return;
  227. done = true;
  228. resolve(self, value);
  229. },
  230. function(reason) {
  231. if (done) return;
  232. done = true;
  233. reject(self, reason);
  234. }
  235. );
  236. } catch (ex) {
  237. if (done) return;
  238. done = true;
  239. reject(self, ex);
  240. }
  241. }
  242. Promise.prototype['catch'] = function(onRejected) {
  243. return this.then(null, onRejected);
  244. };
  245. Promise.prototype.then = function(onFulfilled, onRejected) {
  246. // @ts-ignore
  247. var prom = new this.constructor(noop);
  248. handle(this, new Handler(onFulfilled, onRejected, prom));
  249. return prom;
  250. };
  251. Promise.prototype['finally'] = finallyConstructor;
  252. Promise.all = function(arr) {
  253. return new Promise(function(resolve, reject) {
  254. if (!isArray(arr)) {
  255. return reject(new TypeError('Promise.all accepts an array'));
  256. }
  257. var args = Array.prototype.slice.call(arr);
  258. if (args.length === 0) return resolve([]);
  259. var remaining = args.length;
  260. function res(i, val) {
  261. try {
  262. if (val && (typeof val === 'object' || typeof val === 'function')) {
  263. var then = val.then;
  264. if (typeof then === 'function') {
  265. then.call(
  266. val,
  267. function(val) {
  268. res(i, val);
  269. },
  270. reject
  271. );
  272. return;
  273. }
  274. }
  275. args[i] = val;
  276. if (--remaining === 0) {
  277. resolve(args);
  278. }
  279. } catch (ex) {
  280. reject(ex);
  281. }
  282. }
  283. for (var i = 0; i < args.length; i++) {
  284. res(i, args[i]);
  285. }
  286. });
  287. };
  288. Promise.any = any;
  289. Promise.allSettled = allSettled;
  290. Promise.resolve = function(value) {
  291. if (value && typeof value === 'object' && value.constructor === Promise) {
  292. return value;
  293. }
  294. return new Promise(function(resolve) {
  295. resolve(value);
  296. });
  297. };
  298. Promise.reject = function(value) {
  299. return new Promise(function(resolve, reject) {
  300. reject(value);
  301. });
  302. };
  303. Promise.race = function(arr) {
  304. return new Promise(function(resolve, reject) {
  305. if (!isArray(arr)) {
  306. return reject(new TypeError('Promise.race accepts an array'));
  307. }
  308. for (var i = 0, len = arr.length; i < len; i++) {
  309. Promise.resolve(arr[i]).then(resolve, reject);
  310. }
  311. });
  312. };
  313. // Use polyfill for setImmediate for performance gains
  314. Promise._immediateFn =
  315. // @ts-ignore
  316. (typeof setImmediate === 'function' &&
  317. function(fn) {
  318. // @ts-ignore
  319. setImmediate(fn);
  320. }) ||
  321. function(fn) {
  322. setTimeoutFunc(fn, 0);
  323. };
  324. Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
  325. if (typeof console !== 'undefined' && console) {
  326. console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
  327. }
  328. };
  329. /** @suppress {undefinedVars} */
  330. var globalNS = (function() {
  331. // the only reliable means to get the global object is
  332. // `Function('return this')()`
  333. // However, this causes CSP violations in Chrome apps.
  334. if (typeof self !== 'undefined') {
  335. return self;
  336. }
  337. if (typeof window !== 'undefined') {
  338. return window;
  339. }
  340. if (typeof global !== 'undefined') {
  341. return global;
  342. }
  343. throw new Error('unable to locate global object');
  344. })();
  345. // Expose the polyfill if Promise is undefined or set to a
  346. // non-function value. The latter can be due to a named HTMLElement
  347. // being exposed by browsers for legacy reasons.
  348. // https://github.com/taylorhakes/promise-polyfill/issues/114
  349. if (typeof globalNS['Promise'] !== 'function') {
  350. globalNS['Promise'] = Promise;
  351. } else {
  352. if (!globalNS.Promise.prototype['finally']) {
  353. globalNS.Promise.prototype['finally'] = finallyConstructor;
  354. }
  355. if (!globalNS.Promise.allSettled) {
  356. globalNS.Promise.allSettled = allSettled;
  357. }
  358. if (!globalNS.Promise.any) {
  359. globalNS.Promise.any = any;
  360. }
  361. }