Spaces:
Sleeping
Sleeping
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(16); | |
function noop() {} | |
const PENDING = void 0; | |
const FULFILLED = 1; | |
const REJECTED = 2; | |
const GET_THEN_ERROR = new ErrorObject(); | |
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 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(promise => { | |
var sealed = false; | |
var 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 === 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, selfFulfillment()); | |
} else if (objectOrFunction(value)) { | |
handleMaybeThenable(promise, value, getThen(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) { | |
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 ErrorObject() { | |
this.error = null; | |
} | |
const 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) { | |
let 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); | |
} | |
} | |
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, | |
getThen, | |
noop, | |
resolve, | |
reject, | |
fulfill, | |
subscribe, | |
publish, | |
publishRejection, | |
initializePromise, | |
invokeCallback, | |
FULFILLED, | |
REJECTED, | |
PENDING, | |
handleMaybeThenable | |
}; | |