UNPKG

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