UNPKG

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