UNPKG

4.8 kBJavaScriptView Raw
1'use strict';
2
3
4
5function noop() {}
6
7// States:
8//
9// 0 - pending
10// 1 - fulfilled with _value
11// 2 - rejected with _value
12// 3 - adopted the state of another promise, _value
13//
14// once the state is no longer pending (0) it is immutable
15
16// All `_` prefixed properties will be reduced to `_{random number}`
17// at build time to obfuscate them and discourage their use.
18// We don't use symbols or Object.defineProperty to fully hide them
19// because the performance isn't good enough.
20
21
22// to avoid using try/catch inside critical functions, we
23// extract them to here.
24var LAST_ERROR = null;
25var IS_ERROR = {};
26function getThen(obj) {
27 try {
28 return obj.then;
29 } catch (ex) {
30 LAST_ERROR = ex;
31 return IS_ERROR;
32 }
33}
34
35function tryCallOne(fn, a) {
36 try {
37 return fn(a);
38 } catch (ex) {
39 LAST_ERROR = ex;
40 return IS_ERROR;
41 }
42}
43function tryCallTwo(fn, a, b) {
44 try {
45 fn(a, b);
46 } catch (ex) {
47 LAST_ERROR = ex;
48 return IS_ERROR;
49 }
50}
51
52module.exports = Promise;
53
54function Promise(fn) {
55 if (typeof this !== 'object') {
56 throw new TypeError('Promises must be constructed via new');
57 }
58 if (typeof fn !== 'function') {
59 throw new TypeError('Promise constructor\'s argument is not a function');
60 }
61 this._h = 0;
62 this._i = 0;
63 this._j = null;
64 this._k = null;
65 if (fn === noop) return;
66 doResolve(fn, this);
67}
68Promise._l = null;
69Promise._m = null;
70Promise._n = noop;
71
72Promise.prototype.then = function(onFulfilled, onRejected) {
73 if (this.constructor !== Promise) {
74 return safeThen(this, onFulfilled, onRejected);
75 }
76 var res = new Promise(noop);
77 handle(this, new Handler(onFulfilled, onRejected, res));
78 return res;
79};
80
81function safeThen(self, onFulfilled, onRejected) {
82 return new self.constructor(function (resolve, reject) {
83 var res = new Promise(noop);
84 res.then(resolve, reject);
85 handle(self, new Handler(onFulfilled, onRejected, res));
86 });
87}
88function handle(self, deferred) {
89 while (self._i === 3) {
90 self = self._j;
91 }
92 if (Promise._l) {
93 Promise._l(self);
94 }
95 if (self._i === 0) {
96 if (self._h === 0) {
97 self._h = 1;
98 self._k = deferred;
99 return;
100 }
101 if (self._h === 1) {
102 self._h = 2;
103 self._k = [self._k, deferred];
104 return;
105 }
106 self._k.push(deferred);
107 return;
108 }
109 handleResolved(self, deferred);
110}
111
112function handleResolved(self, deferred) {
113 setImmediate(function() {
114 var cb = self._i === 1 ? deferred.onFulfilled : deferred.onRejected;
115 if (cb === null) {
116 if (self._i === 1) {
117 resolve(deferred.promise, self._j);
118 } else {
119 reject(deferred.promise, self._j);
120 }
121 return;
122 }
123 var ret = tryCallOne(cb, self._j);
124 if (ret === IS_ERROR) {
125 reject(deferred.promise, LAST_ERROR);
126 } else {
127 resolve(deferred.promise, ret);
128 }
129 });
130}
131function resolve(self, newValue) {
132 // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
133 if (newValue === self) {
134 return reject(
135 self,
136 new TypeError('A promise cannot be resolved with itself.')
137 );
138 }
139 if (
140 newValue &&
141 (typeof newValue === 'object' || typeof newValue === 'function')
142 ) {
143 var then = getThen(newValue);
144 if (then === IS_ERROR) {
145 return reject(self, LAST_ERROR);
146 }
147 if (
148 then === self.then &&
149 newValue instanceof Promise
150 ) {
151 self._i = 3;
152 self._j = newValue;
153 finale(self);
154 return;
155 } else if (typeof then === 'function') {
156 doResolve(then.bind(newValue), self);
157 return;
158 }
159 }
160 self._i = 1;
161 self._j = newValue;
162 finale(self);
163}
164
165function reject(self, newValue) {
166 self._i = 2;
167 self._j = newValue;
168 if (Promise._m) {
169 Promise._m(self, newValue);
170 }
171 finale(self);
172}
173function finale(self) {
174 if (self._h === 1) {
175 handle(self, self._k);
176 self._k = null;
177 }
178 if (self._h === 2) {
179 for (var i = 0; i < self._k.length; i++) {
180 handle(self, self._k[i]);
181 }
182 self._k = null;
183 }
184}
185
186function Handler(onFulfilled, onRejected, promise){
187 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
188 this.onRejected = typeof onRejected === 'function' ? onRejected : null;
189 this.promise = promise;
190}
191
192/**
193 * Take a potentially misbehaving resolver function and make sure
194 * onFulfilled and onRejected are only called once.
195 *
196 * Makes no guarantees about asynchrony.
197 */
198function doResolve(fn, promise) {
199 var done = false;
200 var res = tryCallTwo(fn, function (value) {
201 if (done) return;
202 done = true;
203 resolve(promise, value);
204 }, function (reason) {
205 if (done) return;
206 done = true;
207 reject(promise, reason);
208 });
209 if (!done && res === IS_ERROR) {
210 done = true;
211 reject(promise, LAST_ERROR);
212 }
213}