UNPKG

5.79 kBJavaScriptView Raw
1import {
2 objectOrFunction,
3 isFunction
4} from './utils';
5
6import {
7 asap
8} from './asap';
9
10import originalThen from './then';
11import originalResolve from './promise/resolve';
12
13export const PROMISE_ID = Math.random().toString(36).substring(2);
14
15function noop() {}
16
17const PENDING = void 0;
18const FULFILLED = 1;
19const REJECTED = 2;
20
21const TRY_CATCH_ERROR = { error: null };
22
23function selfFulfillment() {
24 return new TypeError("You cannot resolve a promise with itself");
25}
26
27function cannotReturnOwn() {
28 return new TypeError('A promises callback cannot return that same promise.');
29}
30
31function getThen(promise) {
32 try {
33 return promise.then;
34 } catch(error) {
35 TRY_CATCH_ERROR.error = error;
36 return TRY_CATCH_ERROR;
37 }
38}
39
40function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
41 try {
42 then.call(value, fulfillmentHandler, rejectionHandler);
43 } catch(e) {
44 return e;
45 }
46}
47
48function handleForeignThenable(promise, thenable, then) {
49 asap(promise => {
50 var sealed = false;
51 var error = tryThen(then, thenable, value => {
52 if (sealed) { return; }
53 sealed = true;
54 if (thenable !== value) {
55 resolve(promise, value);
56 } else {
57 fulfill(promise, value);
58 }
59 }, reason => {
60 if (sealed) { return; }
61 sealed = true;
62
63 reject(promise, reason);
64 }, 'Settle: ' + (promise._label || ' unknown promise'));
65
66 if (!sealed && error) {
67 sealed = true;
68 reject(promise, error);
69 }
70 }, promise);
71}
72
73function handleOwnThenable(promise, thenable) {
74 if (thenable._state === FULFILLED) {
75 fulfill(promise, thenable._result);
76 } else if (thenable._state === REJECTED) {
77 reject(promise, thenable._result);
78 } else {
79 subscribe(thenable, undefined, value => resolve(promise, value),
80 reason => reject(promise, reason))
81 }
82}
83
84function handleMaybeThenable(promise, maybeThenable, then) {
85 if (maybeThenable.constructor === promise.constructor &&
86 then === originalThen &&
87 maybeThenable.constructor.resolve === originalResolve) {
88 handleOwnThenable(promise, maybeThenable);
89 } else {
90 if (then === TRY_CATCH_ERROR) {
91 reject(promise, TRY_CATCH_ERROR.error);
92 TRY_CATCH_ERROR.error = null;
93 } else if (then === undefined) {
94 fulfill(promise, maybeThenable);
95 } else if (isFunction(then)) {
96 handleForeignThenable(promise, maybeThenable, then);
97 } else {
98 fulfill(promise, maybeThenable);
99 }
100 }
101}
102
103function resolve(promise, value) {
104 if (promise === value) {
105 reject(promise, selfFulfillment());
106 } else if (objectOrFunction(value)) {
107 handleMaybeThenable(promise, value, getThen(value));
108 } else {
109 fulfill(promise, value);
110 }
111}
112
113function publishRejection(promise) {
114 if (promise._onerror) {
115 promise._onerror(promise._result);
116 }
117
118 publish(promise);
119}
120
121function fulfill(promise, value) {
122 if (promise._state !== PENDING) { return; }
123
124 promise._result = value;
125 promise._state = FULFILLED;
126
127 if (promise._subscribers.length !== 0) {
128 asap(publish, promise);
129 }
130}
131
132function reject(promise, reason) {
133 if (promise._state !== PENDING) { return; }
134 promise._state = REJECTED;
135 promise._result = reason;
136
137 asap(publishRejection, promise);
138}
139
140function subscribe(parent, child, onFulfillment, onRejection) {
141 let { _subscribers } = parent;
142 let { length } = _subscribers;
143
144 parent._onerror = null;
145
146 _subscribers[length] = child;
147 _subscribers[length + FULFILLED] = onFulfillment;
148 _subscribers[length + REJECTED] = onRejection;
149
150 if (length === 0 && parent._state) {
151 asap(publish, parent);
152 }
153}
154
155function publish(promise) {
156 let subscribers = promise._subscribers;
157 let settled = promise._state;
158
159 if (subscribers.length === 0) { return; }
160
161 let child, callback, detail = promise._result;
162
163 for (let i = 0; i < subscribers.length; i += 3) {
164 child = subscribers[i];
165 callback = subscribers[i + settled];
166
167 if (child) {
168 invokeCallback(settled, child, callback, detail);
169 } else {
170 callback(detail);
171 }
172 }
173
174 promise._subscribers.length = 0;
175}
176
177
178function tryCatch(callback, detail) {
179 try {
180 return callback(detail);
181 } catch(e) {
182 TRY_CATCH_ERROR.error = e;
183 return TRY_CATCH_ERROR;
184 }
185}
186
187function invokeCallback(settled, promise, callback, detail) {
188 let hasCallback = isFunction(callback),
189 value, error, succeeded, failed;
190
191 if (hasCallback) {
192 value = tryCatch(callback, detail);
193
194 if (value === TRY_CATCH_ERROR) {
195 failed = true;
196 error = value.error;
197 value.error = null;
198 } else {
199 succeeded = true;
200 }
201
202 if (promise === value) {
203 reject(promise, cannotReturnOwn());
204 return;
205 }
206
207 } else {
208 value = detail;
209 succeeded = true;
210 }
211
212 if (promise._state !== PENDING) {
213 // noop
214 } else if (hasCallback && succeeded) {
215 resolve(promise, value);
216 } else if (failed) {
217 reject(promise, error);
218 } else if (settled === FULFILLED) {
219 fulfill(promise, value);
220 } else if (settled === REJECTED) {
221 reject(promise, value);
222 }
223}
224
225function initializePromise(promise, resolver) {
226 try {
227 resolver(function resolvePromise(value){
228 resolve(promise, value);
229 }, function rejectPromise(reason) {
230 reject(promise, reason);
231 });
232 } catch(e) {
233 reject(promise, e);
234 }
235}
236
237let id = 0;
238function nextId() {
239 return id++;
240}
241
242function makePromise(promise) {
243 promise[PROMISE_ID] = id++;
244 promise._state = undefined;
245 promise._result = undefined;
246 promise._subscribers = [];
247}
248
249export {
250 nextId,
251 makePromise,
252 getThen,
253 noop,
254 resolve,
255 reject,
256 fulfill,
257 subscribe,
258 publish,
259 publishRejection,
260 initializePromise,
261 invokeCallback,
262 FULFILLED,
263 REJECTED,
264 PENDING,
265 handleMaybeThenable
266};