UNPKG

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