1 | import {
|
2 | objectOrFunction,
|
3 | isFunction
|
4 | } from './utils';
|
5 |
|
6 | import {
|
7 | asap
|
8 | } from './asap';
|
9 |
|
10 | import originalThen from './then';
|
11 | import originalResolve from './promise/resolve';
|
12 |
|
13 | export const PROMISE_ID = Math.random().toString(36).substring(2);
|
14 |
|
15 | function noop() {}
|
16 |
|
17 | const PENDING = void 0;
|
18 | const FULFILLED = 1;
|
19 | const REJECTED = 2;
|
20 |
|
21 | const TRY_CATCH_ERROR = { error: null };
|
22 |
|
23 | function selfFulfillment() {
|
24 | return new TypeError("You cannot resolve a promise with itself");
|
25 | }
|
26 |
|
27 | function cannotReturnOwn() {
|
28 | return new TypeError('A promises callback cannot return that same promise.');
|
29 | }
|
30 |
|
31 | function 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 |
|
40 | function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
|
41 | try {
|
42 | then.call(value, fulfillmentHandler, rejectionHandler);
|
43 | } catch(e) {
|
44 | return e;
|
45 | }
|
46 | }
|
47 |
|
48 | function 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 |
|
73 | function 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 |
|
84 | function 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 |
|
103 | function 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 |
|
113 | function publishRejection(promise) {
|
114 | if (promise._onerror) {
|
115 | promise._onerror(promise._result);
|
116 | }
|
117 |
|
118 | publish(promise);
|
119 | }
|
120 |
|
121 | function 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 |
|
132 | function reject(promise, reason) {
|
133 | if (promise._state !== PENDING) { return; }
|
134 | promise._state = REJECTED;
|
135 | promise._result = reason;
|
136 |
|
137 | asap(publishRejection, promise);
|
138 | }
|
139 |
|
140 | function 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 |
|
155 | function 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 |
|
178 | function 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 |
|
187 | function 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 |
|
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 |
|
225 | function 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 |
|
237 | let id = 0;
|
238 | function nextId() {
|
239 | return id++;
|
240 | }
|
241 |
|
242 | function makePromise(promise) {
|
243 | promise[PROMISE_ID] = id++;
|
244 | promise._state = undefined;
|
245 | promise._result = undefined;
|
246 | promise._subscribers = [];
|
247 | }
|
248 |
|
249 | export {
|
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 | };
|