UNPKG

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