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