| import { |
| objectOrFunction, |
| isFunction |
| } from './utils'; |
| |
| import asap from './asap'; |
| |
| function noop() {} |
| |
| var PENDING = void 0; |
| var FULFILLED = 1; |
| var REJECTED = 2; |
| |
| var GET_THEN_ERROR = new ErrorObject(); |
| |
| function selfFullfillment() { |
| return new TypeError("You cannot resolve a promise with itself"); |
| } |
| |
| function cannotReturnOwn() { |
| return new TypeError('A promises callback cannot return that same promise.') |
| } |
| |
| function getThen(promise) { |
| try { |
| return promise.then; |
| } catch(error) { |
| GET_THEN_ERROR.error = error; |
| return GET_THEN_ERROR; |
| } |
| } |
| |
| function tryThen(then, value, fulfillmentHandler, rejectionHandler) { |
| try { |
| then.call(value, fulfillmentHandler, rejectionHandler); |
| } catch(e) { |
| return e; |
| } |
| } |
| |
| function handleForeignThenable(promise, thenable, then) { |
| asap(function(promise) { |
| var sealed = false; |
| var error = tryThen(then, thenable, function(value) { |
| if (sealed) { return; } |
| sealed = true; |
| if (thenable !== value) { |
| resolve(promise, value); |
| } else { |
| fulfill(promise, value); |
| } |
| }, function(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 (promise._state === REJECTED) { |
| reject(promise, thenable._result); |
| } else { |
| subscribe(thenable, undefined, function(value) { |
| resolve(promise, value); |
| }, function(reason) { |
| reject(promise, reason); |
| }); |
| } |
| } |
| |
| function handleMaybeThenable(promise, maybeThenable) { |
| if (maybeThenable.constructor === promise.constructor) { |
| handleOwnThenable(promise, maybeThenable); |
| } else { |
| var then = getThen(maybeThenable); |
| |
| if (then === GET_THEN_ERROR) { |
| reject(promise, GET_THEN_ERROR.error); |
| } 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, selfFullfillment()); |
| } else if (objectOrFunction(value)) { |
| handleMaybeThenable(promise, value); |
| } 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) { |
| } else { |
| 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) { |
| var subscribers = parent._subscribers; |
| var length = subscribers.length; |
| |
| 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) { |
| var subscribers = promise._subscribers; |
| var settled = promise._state; |
| |
| if (subscribers.length === 0) { return; } |
| |
| var child, callback, detail = promise._result; |
| |
| for (var 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 ErrorObject() { |
| this.error = null; |
| } |
| |
| var TRY_CATCH_ERROR = new ErrorObject(); |
| |
| function tryCatch(callback, detail) { |
| try { |
| return callback(detail); |
| } catch(e) { |
| TRY_CATCH_ERROR.error = e; |
| return TRY_CATCH_ERROR; |
| } |
| } |
| |
| function invokeCallback(settled, promise, callback, detail) { |
| var hasCallback = isFunction(callback), |
| value, error, succeeded, failed; |
| |
| if (hasCallback) { |
| value = tryCatch(callback, detail); |
| |
| if (value === TRY_CATCH_ERROR) { |
| failed = true; |
| error = value.error; |
| value = null; |
| } else { |
| succeeded = true; |
| } |
| |
| if (promise === value) { |
| reject(promise, cannotReturnOwn()); |
| return; |
| } |
| |
| } else { |
| value = detail; |
| succeeded = true; |
| } |
| |
| if (promise._state !== PENDING) { |
| // noop |
| } else if (hasCallback && succeeded) { |
| resolve(promise, value); |
| } else if (failed) { |
| 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); |
| } |
| } |
| |
| export { |
| noop, |
| resolve, |
| reject, |
| fulfill, |
| subscribe, |
| publish, |
| publishRejection, |
| initializePromise, |
| invokeCallback, |
| FULFILLED, |
| REJECTED, |
| PENDING |
| }; |