| import { |
| objectOrFunction, |
| isFunction |
| } from './utils'; |
| |
| import { |
| asap |
| } from './asap'; |
| |
| import originalThen from './then'; |
| import originalResolve from './promise/resolve'; |
| |
| export const PROMISE_ID = Math.random().toString(36).substring(2); |
| |
| function noop() {} |
| |
| const PENDING = void 0; |
| const FULFILLED = 1; |
| const REJECTED = 2; |
| |
| function selfFulfillment() { |
| return new TypeError("You cannot resolve a promise with itself"); |
| } |
| |
| function cannotReturnOwn() { |
| return new TypeError('A promises callback cannot return that same promise.'); |
| } |
| |
| function tryThen(then, value, fulfillmentHandler, rejectionHandler) { |
| try { |
| then.call(value, fulfillmentHandler, rejectionHandler); |
| } catch(e) { |
| return e; |
| } |
| } |
| |
| function handleForeignThenable(promise, thenable, then) { |
| asap(promise => { |
| let sealed = false; |
| let error = tryThen(then, thenable, value => { |
| if (sealed) { return; } |
| sealed = true; |
| if (thenable !== value) { |
| resolve(promise, value); |
| } else { |
| fulfill(promise, value); |
| } |
| }, reason => { |
| if (sealed) { return; } |
| sealed = true; |
| |
| reject(promise, reason); |
| }, 'Settle: ' + (promise._label || ' unknown promise')); |
| |
| if (!sealed && error) { |
| sealed = true; |
| reject(promise, error); |
| } |
| }, promise); |
| } |
| |
| function handleOwnThenable(promise, thenable) { |
| if (thenable._state === FULFILLED) { |
| fulfill(promise, thenable._result); |
| } else if (thenable._state === REJECTED) { |
| reject(promise, thenable._result); |
| } else { |
| subscribe(thenable, undefined, value => resolve(promise, value), |
| reason => reject(promise, reason)) |
| } |
| } |
| |
| function handleMaybeThenable(promise, maybeThenable, then) { |
| if (maybeThenable.constructor === promise.constructor && |
| then === originalThen && |
| maybeThenable.constructor.resolve === originalResolve) { |
| handleOwnThenable(promise, maybeThenable); |
| } else { |
| if (then === undefined) { |
| fulfill(promise, maybeThenable); |
| } else if (isFunction(then)) { |
| handleForeignThenable(promise, maybeThenable, then); |
| } else { |
| fulfill(promise, maybeThenable); |
| } |
| } |
| } |
| |
| function resolve(promise, value) { |
| if (promise === value) { |
| reject(promise, selfFulfillment()); |
| } else if (objectOrFunction(value)) { |
| let then; |
| try { |
| then = value.then; |
| } catch (error) { |
| reject(promise, error); |
| return; |
| } |
| handleMaybeThenable(promise, value, then); |
| } else { |
| fulfill(promise, value); |
| } |
| } |
| |
| function publishRejection(promise) { |
| if (promise._onerror) { |
| promise._onerror(promise._result); |
| } |
| |
| publish(promise); |
| } |
| |
| function fulfill(promise, value) { |
| if (promise._state !== PENDING) { return; } |
| |
| promise._result = value; |
| promise._state = FULFILLED; |
| |
| if (promise._subscribers.length !== 0) { |
| asap(publish, promise); |
| } |
| } |
| |
| function reject(promise, reason) { |
| if (promise._state !== PENDING) { return; } |
| promise._state = REJECTED; |
| promise._result = reason; |
| |
| asap(publishRejection, promise); |
| } |
| |
| function subscribe(parent, child, onFulfillment, onRejection) { |
| let { _subscribers } = parent; |
| let { length } = _subscribers; |
| |
| parent._onerror = null; |
| |
| _subscribers[length] = child; |
| _subscribers[length + FULFILLED] = onFulfillment; |
| _subscribers[length + REJECTED] = onRejection; |
| |
| if (length === 0 && parent._state) { |
| asap(publish, parent); |
| } |
| } |
| |
| function publish(promise) { |
| let subscribers = promise._subscribers; |
| let settled = promise._state; |
| |
| if (subscribers.length === 0) { return; } |
| |
| let child, callback, detail = promise._result; |
| |
| for (let i = 0; i < subscribers.length; i += 3) { |
| child = subscribers[i]; |
| callback = subscribers[i + settled]; |
| |
| if (child) { |
| invokeCallback(settled, child, callback, detail); |
| } else { |
| callback(detail); |
| } |
| } |
| |
| promise._subscribers.length = 0; |
| } |
| |
| function invokeCallback(settled, promise, callback, detail) { |
| let hasCallback = isFunction(callback), |
| value, error, succeeded = true; |
| |
| if (hasCallback) { |
| try { |
| value = callback(detail); |
| } catch (e) { |
| succeeded = false; |
| error = e; |
| } |
| |
| if (promise === value) { |
| reject(promise, cannotReturnOwn()); |
| return; |
| } |
| } else { |
| value = detail; |
| } |
| |
| if (promise._state !== PENDING) { |
| // noop |
| } else if (hasCallback && succeeded) { |
| resolve(promise, value); |
| } else if (succeeded === false) { |
| reject(promise, error); |
| } else if (settled === FULFILLED) { |
| fulfill(promise, value); |
| } else if (settled === REJECTED) { |
| reject(promise, value); |
| } |
| } |
| |
| function initializePromise(promise, resolver) { |
| try { |
| resolver(function resolvePromise(value){ |
| resolve(promise, value); |
| }, function rejectPromise(reason) { |
| reject(promise, reason); |
| }); |
| } catch(e) { |
| reject(promise, e); |
| } |
| } |
| |
| let id = 0; |
| function nextId() { |
| return id++; |
| } |
| |
| function makePromise(promise) { |
| promise[PROMISE_ID] = id++; |
| promise._state = undefined; |
| promise._result = undefined; |
| promise._subscribers = []; |
| } |
| |
| export { |
| nextId, |
| makePromise, |
| noop, |
| resolve, |
| reject, |
| fulfill, |
| subscribe, |
| publish, |
| publishRejection, |
| initializePromise, |
| invokeCallback, |
| FULFILLED, |
| REJECTED, |
| PENDING, |
| handleMaybeThenable |
| }; |