import originalThen from './then'; import originalResolve from './promise/resolve'; import instrument from './instrument'; import { config } from './config'; import Promise from './promise'; function withOwnPromise() { return new TypeError('A promises callback cannot return that same promise.'); } function objectOrFunction(x) { let type = typeof x; return x !== null && (type === 'object' || type === 'function'); } export function noop() {} export const PENDING = void 0; export const FULFILLED = 1; export const REJECTED = 2; function tryThen(then, value, fulfillmentHandler, rejectionHandler) { try { then.call(value, fulfillmentHandler, rejectionHandler); } catch(e) { return e; } } function handleForeignThenable(promise, thenable, then) { config.async(promise => { let sealed = false; let error = tryThen(then, thenable, value => { if (sealed) { return; } sealed = true; if (thenable === value) { fulfill(promise, value); } else { resolve(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) { thenable._onError = null; reject(promise, thenable._result); } else { subscribe(thenable, undefined, value => { if (thenable === value) { fulfill(promise, value); } else { resolve(promise, value); } }, reason => reject(promise, reason)); } } export function handleMaybeThenable(promise, maybeThenable, then) { let isOwnThenable = maybeThenable.constructor === promise.constructor && then === originalThen && promise.constructor.resolve === originalResolve; if (isOwnThenable) { handleOwnThenable(promise, maybeThenable); } else if (typeof then === 'function') { handleForeignThenable(promise, maybeThenable, then); } else { fulfill(promise, maybeThenable); } } export function resolve(promise, value) { if (promise === value) { fulfill(promise, value); } else if (objectOrFunction(value)) { let then; try { then = value.then; } catch (error) { reject(promise, error); return; } handleMaybeThenable(promise, value, then); } else { fulfill(promise, value); } } export function publishRejection(promise) { if (promise._onError) { promise._onError(promise._result); } publish(promise); } export function fulfill(promise, value) { if (promise._state !== PENDING) { return; } promise._result = value; promise._state = FULFILLED; if (promise._subscribers.length === 0) { if (config.instrument) { instrument('fulfilled', promise); } } else { config.async(publish, promise); } } export function reject(promise, reason) { if (promise._state !== PENDING) { return; } promise._state = REJECTED; promise._result = reason; config.async(publishRejection, promise); } export function subscribe(parent, child, onFulfillment, onRejection) { let subscribers = parent._subscribers; let length = subscribers.length; parent._onError = null; subscribers[length] = child; subscribers[length + FULFILLED] = onFulfillment; subscribers[length + REJECTED] = onRejection; if (length === 0 && parent._state) { config.async(publish, parent); } } export function publish(promise) { let subscribers = promise._subscribers; let settled = promise._state; if (config.instrument) { instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); } if (subscribers.length === 0) { return; } let child, callback, result = promise._result; for (let i = 0; i < subscribers.length; i += 3) { child = subscribers[i]; callback = subscribers[i + settled]; if (child) { invokeCallback(settled, child, callback, result); } else { callback(result); } } promise._subscribers.length = 0; } export function invokeCallback(state, promise, callback, result) { let hasCallback = typeof callback === 'function'; let value, succeeded = true, error; if (hasCallback) { try { value = callback(result) } catch (e) { succeeded = false; error = e; } } else { value = result; } if (promise._state !== PENDING) { // noop } else if (value === promise) { reject(promise, withOwnPromise()); } else if (succeeded === false) { reject(promise, error); } else if (hasCallback) { resolve(promise, value); } else if (state === FULFILLED) { fulfill(promise, value); } else if (state === REJECTED) { reject(promise, value); } } export function initializePromise(promise, resolver) { let resolved = false; try { resolver(value => { if (resolved) { return; } resolved = true; resolve(promise, value); }, reason => { if (resolved) { return; } resolved = true; reject(promise, reason); }); } catch(e) { reject(promise, e); } }