UNPKG

4.25 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('not a function');
60 }
61 this._41 = 0;
62 this._86 = null;
63 this._17 = [];
64 if (fn === noop) return;
65 doResolve(fn, this);
66}
67Promise._1 = noop;
68
69Promise.prototype.then = function(onFulfilled, onRejected) {
70 if (this.constructor !== Promise) {
71 return safeThen(this, onFulfilled, onRejected);
72 }
73 var res = new Promise(noop);
74 handle(this, new Handler(onFulfilled, onRejected, res));
75 return res;
76};
77
78function safeThen(self, onFulfilled, onRejected) {
79 return new self.constructor(function (resolve, reject) {
80 var res = new Promise(noop);
81 res.then(resolve, reject);
82 handle(self, new Handler(onFulfilled, onRejected, res));
83 });
84};
85function handle(self, deferred) {
86 while (self._41 === 3) {
87 self = self._86;
88 }
89 if (self._41 === 0) {
90 self._17.push(deferred);
91 return;
92 }
93 setImmediate(function() {
94 var cb = self._41 === 1 ? deferred.onFulfilled : deferred.onRejected;
95 if (cb === null) {
96 if (self._41 === 1) {
97 resolve(deferred.promise, self._86);
98 } else {
99 reject(deferred.promise, self._86);
100 }
101 return;
102 }
103 var ret = tryCallOne(cb, self._86);
104 if (ret === IS_ERROR) {
105 reject(deferred.promise, LAST_ERROR);
106 } else {
107 resolve(deferred.promise, ret);
108 }
109 });
110}
111function resolve(self, newValue) {
112 // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
113 if (newValue === self) {
114 return reject(
115 self,
116 new TypeError('A promise cannot be resolved with itself.')
117 );
118 }
119 if (
120 newValue &&
121 (typeof newValue === 'object' || typeof newValue === 'function')
122 ) {
123 var then = getThen(newValue);
124 if (then === IS_ERROR) {
125 return reject(self, LAST_ERROR);
126 }
127 if (
128 then === self.then &&
129 newValue instanceof Promise
130 ) {
131 self._41 = 3;
132 self._86 = newValue;
133 finale(self);
134 return;
135 } else if (typeof then === 'function') {
136 doResolve(then.bind(newValue), self);
137 return;
138 }
139 }
140 self._41 = 1;
141 self._86 = newValue;
142 finale(self);
143}
144
145function reject(self, newValue) {
146 self._41 = 2;
147 self._86 = newValue;
148 finale(self);
149}
150function finale(self) {
151 for (var i = 0; i < self._17.length; i++) {
152 handle(self, self._17[i]);
153 }
154 self._17 = null;
155}
156
157function Handler(onFulfilled, onRejected, promise){
158 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
159 this.onRejected = typeof onRejected === 'function' ? onRejected : null;
160 this.promise = promise;
161}
162
163/**
164 * Take a potentially misbehaving resolver function and make sure
165 * onFulfilled and onRejected are only called once.
166 *
167 * Makes no guarantees about asynchrony.
168 */
169function doResolve(fn, promise) {
170 var done = false;
171 var res = tryCallTwo(fn, function (value) {
172 if (done) return;
173 done = true;
174 resolve(promise, value);
175 }, function (reason) {
176 if (done) return;
177 done = true;
178 reject(promise, reason);
179 })
180 if (!done && res === IS_ERROR) {
181 done = true;
182 reject(promise, LAST_ERROR);
183 }
184}