UNPKG

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