UNPKG

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