aeebf57665485168bc492ad975ce54f88b8717b0
[aai/esr-gui.git] /
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 var PROMISE_ID = Math.random().toString(36).substring(16);
14
15 function noop() {}
16
17 var PENDING   = void 0;
18 var FULFILLED = 1;
19 var REJECTED  = 2;
20
21 var GET_THEN_ERROR = new ErrorObject();
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     GET_THEN_ERROR.error = error;
36     return GET_THEN_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(function(promise) {
50     var sealed = false;
51     var error = tryThen(then, thenable, function(value) {
52       if (sealed) { return; }
53       sealed = true;
54       if (thenable !== value) {
55         resolve(promise, value);
56       } else {
57         fulfill(promise, value);
58       }
59     }, function(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, function(value) {
80       resolve(promise, value);
81     }, function(reason) {
82       reject(promise, reason);
83     });
84   }
85 }
86
87 function handleMaybeThenable(promise, maybeThenable, then) {
88   if (maybeThenable.constructor === promise.constructor &&
89       then === originalThen &&
90       constructor.resolve === originalResolve) {
91     handleOwnThenable(promise, maybeThenable);
92   } else {
93     if (then === GET_THEN_ERROR) {
94       reject(promise, GET_THEN_ERROR.error);
95     } else if (then === undefined) {
96       fulfill(promise, maybeThenable);
97     } else if (isFunction(then)) {
98       handleForeignThenable(promise, maybeThenable, then);
99     } else {
100       fulfill(promise, maybeThenable);
101     }
102   }
103 }
104
105 function resolve(promise, value) {
106   if (promise === value) {
107     reject(promise, selfFulfillment());
108   } else if (objectOrFunction(value)) {
109     handleMaybeThenable(promise, value, getThen(value));
110   } else {
111     fulfill(promise, value);
112   }
113 }
114
115 function publishRejection(promise) {
116   if (promise._onerror) {
117     promise._onerror(promise._result);
118   }
119
120   publish(promise);
121 }
122
123 function fulfill(promise, value) {
124   if (promise._state !== PENDING) { return; }
125
126   promise._result = value;
127   promise._state = FULFILLED;
128
129   if (promise._subscribers.length !== 0) {
130     asap(publish, promise);
131   }
132 }
133
134 function reject(promise, reason) {
135   if (promise._state !== PENDING) { return; }
136   promise._state = REJECTED;
137   promise._result = reason;
138
139   asap(publishRejection, promise);
140 }
141
142 function subscribe(parent, child, onFulfillment, onRejection) {
143   var subscribers = parent._subscribers;
144   var length = subscribers.length;
145
146   parent._onerror = null;
147
148   subscribers[length] = child;
149   subscribers[length + FULFILLED] = onFulfillment;
150   subscribers[length + REJECTED]  = onRejection;
151
152   if (length === 0 && parent._state) {
153     asap(publish, parent);
154   }
155 }
156
157 function publish(promise) {
158   var subscribers = promise._subscribers;
159   var settled = promise._state;
160
161   if (subscribers.length === 0) { return; }
162
163   var child, callback, detail = promise._result;
164
165   for (var i = 0; i < subscribers.length; i += 3) {
166     child = subscribers[i];
167     callback = subscribers[i + settled];
168
169     if (child) {
170       invokeCallback(settled, child, callback, detail);
171     } else {
172       callback(detail);
173     }
174   }
175
176   promise._subscribers.length = 0;
177 }
178
179 function ErrorObject() {
180   this.error = null;
181 }
182
183 var TRY_CATCH_ERROR = new ErrorObject();
184
185 function tryCatch(callback, detail) {
186   try {
187     return callback(detail);
188   } catch(e) {
189     TRY_CATCH_ERROR.error = e;
190     return TRY_CATCH_ERROR;
191   }
192 }
193
194 function invokeCallback(settled, promise, callback, detail) {
195   var hasCallback = isFunction(callback),
196       value, error, succeeded, failed;
197
198   if (hasCallback) {
199     value = tryCatch(callback, detail);
200
201     if (value === TRY_CATCH_ERROR) {
202       failed = true;
203       error = value.error;
204       value = null;
205     } else {
206       succeeded = true;
207     }
208
209     if (promise === value) {
210       reject(promise, cannotReturnOwn());
211       return;
212     }
213
214   } else {
215     value = detail;
216     succeeded = true;
217   }
218
219   if (promise._state !== PENDING) {
220     // noop
221   } else if (hasCallback && succeeded) {
222     resolve(promise, value);
223   } else if (failed) {
224     reject(promise, error);
225   } else if (settled === FULFILLED) {
226     fulfill(promise, value);
227   } else if (settled === REJECTED) {
228     reject(promise, value);
229   }
230 }
231
232 function initializePromise(promise, resolver) {
233   try {
234     resolver(function resolvePromise(value){
235       resolve(promise, value);
236     }, function rejectPromise(reason) {
237       reject(promise, reason);
238     });
239   } catch(e) {
240     reject(promise, e);
241   }
242 }
243
244 var id = 0;
245 function nextId() {
246   return id++;
247 }
248
249 function makePromise(promise) {
250   promise[PROMISE_ID] = id++;
251   promise._state = undefined;
252   promise._result = undefined;
253   promise._subscribers = [];
254 }
255
256 export {
257   nextId,
258   makePromise,
259   getThen,
260   noop,
261   resolve,
262   reject,
263   fulfill,
264   subscribe,
265   publish,
266   publishRejection,
267   initializePromise,
268   invokeCallback,
269   FULFILLED,
270   REJECTED,
271   PENDING,
272   handleMaybeThenable
273 };