You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.5 KiB
288 lines
9.5 KiB
'use strict'; |
|
var $ = require('../internals/export'); |
|
var IS_PURE = require('../internals/is-pure'); |
|
var IS_NODE = require('../internals/engine-is-node'); |
|
var global = require('../internals/global'); |
|
var call = require('../internals/function-call'); |
|
var defineBuiltIn = require('../internals/define-built-in'); |
|
var setPrototypeOf = require('../internals/object-set-prototype-of'); |
|
var setToStringTag = require('../internals/set-to-string-tag'); |
|
var setSpecies = require('../internals/set-species'); |
|
var aCallable = require('../internals/a-callable'); |
|
var isCallable = require('../internals/is-callable'); |
|
var isObject = require('../internals/is-object'); |
|
var anInstance = require('../internals/an-instance'); |
|
var speciesConstructor = require('../internals/species-constructor'); |
|
var task = require('../internals/task').set; |
|
var microtask = require('../internals/microtask'); |
|
var hostReportErrors = require('../internals/host-report-errors'); |
|
var perform = require('../internals/perform'); |
|
var Queue = require('../internals/queue'); |
|
var InternalStateModule = require('../internals/internal-state'); |
|
var NativePromiseConstructor = require('../internals/promise-native-constructor'); |
|
var PromiseConstructorDetection = require('../internals/promise-constructor-detection'); |
|
var newPromiseCapabilityModule = require('../internals/new-promise-capability'); |
|
|
|
var PROMISE = 'Promise'; |
|
var FORCED_PROMISE_CONSTRUCTOR = PromiseConstructorDetection.CONSTRUCTOR; |
|
var NATIVE_PROMISE_REJECTION_EVENT = PromiseConstructorDetection.REJECTION_EVENT; |
|
var NATIVE_PROMISE_SUBCLASSING = PromiseConstructorDetection.SUBCLASSING; |
|
var getInternalPromiseState = InternalStateModule.getterFor(PROMISE); |
|
var setInternalState = InternalStateModule.set; |
|
var NativePromisePrototype = NativePromiseConstructor && NativePromiseConstructor.prototype; |
|
var PromiseConstructor = NativePromiseConstructor; |
|
var PromisePrototype = NativePromisePrototype; |
|
var TypeError = global.TypeError; |
|
var document = global.document; |
|
var process = global.process; |
|
var newPromiseCapability = newPromiseCapabilityModule.f; |
|
var newGenericPromiseCapability = newPromiseCapability; |
|
|
|
var DISPATCH_EVENT = !!(document && document.createEvent && global.dispatchEvent); |
|
var UNHANDLED_REJECTION = 'unhandledrejection'; |
|
var REJECTION_HANDLED = 'rejectionhandled'; |
|
var PENDING = 0; |
|
var FULFILLED = 1; |
|
var REJECTED = 2; |
|
var HANDLED = 1; |
|
var UNHANDLED = 2; |
|
|
|
var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen; |
|
|
|
// helpers |
|
var isThenable = function (it) { |
|
var then; |
|
return isObject(it) && isCallable(then = it.then) ? then : false; |
|
}; |
|
|
|
var callReaction = function (reaction, state) { |
|
var value = state.value; |
|
var ok = state.state == FULFILLED; |
|
var handler = ok ? reaction.ok : reaction.fail; |
|
var resolve = reaction.resolve; |
|
var reject = reaction.reject; |
|
var domain = reaction.domain; |
|
var result, then, exited; |
|
try { |
|
if (handler) { |
|
if (!ok) { |
|
if (state.rejection === UNHANDLED) onHandleUnhandled(state); |
|
state.rejection = HANDLED; |
|
} |
|
if (handler === true) result = value; |
|
else { |
|
if (domain) domain.enter(); |
|
result = handler(value); // can throw |
|
if (domain) { |
|
domain.exit(); |
|
exited = true; |
|
} |
|
} |
|
if (result === reaction.promise) { |
|
reject(TypeError('Promise-chain cycle')); |
|
} else if (then = isThenable(result)) { |
|
call(then, result, resolve, reject); |
|
} else resolve(result); |
|
} else reject(value); |
|
} catch (error) { |
|
if (domain && !exited) domain.exit(); |
|
reject(error); |
|
} |
|
}; |
|
|
|
var notify = function (state, isReject) { |
|
if (state.notified) return; |
|
state.notified = true; |
|
microtask(function () { |
|
var reactions = state.reactions; |
|
var reaction; |
|
while (reaction = reactions.get()) { |
|
callReaction(reaction, state); |
|
} |
|
state.notified = false; |
|
if (isReject && !state.rejection) onUnhandled(state); |
|
}); |
|
}; |
|
|
|
var dispatchEvent = function (name, promise, reason) { |
|
var event, handler; |
|
if (DISPATCH_EVENT) { |
|
event = document.createEvent('Event'); |
|
event.promise = promise; |
|
event.reason = reason; |
|
event.initEvent(name, false, true); |
|
global.dispatchEvent(event); |
|
} else event = { promise: promise, reason: reason }; |
|
if (!NATIVE_PROMISE_REJECTION_EVENT && (handler = global['on' + name])) handler(event); |
|
else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason); |
|
}; |
|
|
|
var onUnhandled = function (state) { |
|
call(task, global, function () { |
|
var promise = state.facade; |
|
var value = state.value; |
|
var IS_UNHANDLED = isUnhandled(state); |
|
var result; |
|
if (IS_UNHANDLED) { |
|
result = perform(function () { |
|
if (IS_NODE) { |
|
process.emit('unhandledRejection', value, promise); |
|
} else dispatchEvent(UNHANDLED_REJECTION, promise, value); |
|
}); |
|
// Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should |
|
state.rejection = IS_NODE || isUnhandled(state) ? UNHANDLED : HANDLED; |
|
if (result.error) throw result.value; |
|
} |
|
}); |
|
}; |
|
|
|
var isUnhandled = function (state) { |
|
return state.rejection !== HANDLED && !state.parent; |
|
}; |
|
|
|
var onHandleUnhandled = function (state) { |
|
call(task, global, function () { |
|
var promise = state.facade; |
|
if (IS_NODE) { |
|
process.emit('rejectionHandled', promise); |
|
} else dispatchEvent(REJECTION_HANDLED, promise, state.value); |
|
}); |
|
}; |
|
|
|
var bind = function (fn, state, unwrap) { |
|
return function (value) { |
|
fn(state, value, unwrap); |
|
}; |
|
}; |
|
|
|
var internalReject = function (state, value, unwrap) { |
|
if (state.done) return; |
|
state.done = true; |
|
if (unwrap) state = unwrap; |
|
state.value = value; |
|
state.state = REJECTED; |
|
notify(state, true); |
|
}; |
|
|
|
var internalResolve = function (state, value, unwrap) { |
|
if (state.done) return; |
|
state.done = true; |
|
if (unwrap) state = unwrap; |
|
try { |
|
if (state.facade === value) throw TypeError("Promise can't be resolved itself"); |
|
var then = isThenable(value); |
|
if (then) { |
|
microtask(function () { |
|
var wrapper = { done: false }; |
|
try { |
|
call(then, value, |
|
bind(internalResolve, wrapper, state), |
|
bind(internalReject, wrapper, state) |
|
); |
|
} catch (error) { |
|
internalReject(wrapper, error, state); |
|
} |
|
}); |
|
} else { |
|
state.value = value; |
|
state.state = FULFILLED; |
|
notify(state, false); |
|
} |
|
} catch (error) { |
|
internalReject({ done: false }, error, state); |
|
} |
|
}; |
|
|
|
// constructor polyfill |
|
if (FORCED_PROMISE_CONSTRUCTOR) { |
|
// 25.4.3.1 Promise(executor) |
|
PromiseConstructor = function Promise(executor) { |
|
anInstance(this, PromisePrototype); |
|
aCallable(executor); |
|
call(Internal, this); |
|
var state = getInternalPromiseState(this); |
|
try { |
|
executor(bind(internalResolve, state), bind(internalReject, state)); |
|
} catch (error) { |
|
internalReject(state, error); |
|
} |
|
}; |
|
|
|
PromisePrototype = PromiseConstructor.prototype; |
|
|
|
// eslint-disable-next-line no-unused-vars -- required for `.length` |
|
Internal = function Promise(executor) { |
|
setInternalState(this, { |
|
type: PROMISE, |
|
done: false, |
|
notified: false, |
|
parent: false, |
|
reactions: new Queue(), |
|
rejection: false, |
|
state: PENDING, |
|
value: undefined |
|
}); |
|
}; |
|
|
|
// `Promise.prototype.then` method |
|
// https://tc39.es/ecma262/#sec-promise.prototype.then |
|
Internal.prototype = defineBuiltIn(PromisePrototype, 'then', function then(onFulfilled, onRejected) { |
|
var state = getInternalPromiseState(this); |
|
var reaction = newPromiseCapability(speciesConstructor(this, PromiseConstructor)); |
|
state.parent = true; |
|
reaction.ok = isCallable(onFulfilled) ? onFulfilled : true; |
|
reaction.fail = isCallable(onRejected) && onRejected; |
|
reaction.domain = IS_NODE ? process.domain : undefined; |
|
if (state.state == PENDING) state.reactions.add(reaction); |
|
else microtask(function () { |
|
callReaction(reaction, state); |
|
}); |
|
return reaction.promise; |
|
}); |
|
|
|
OwnPromiseCapability = function () { |
|
var promise = new Internal(); |
|
var state = getInternalPromiseState(promise); |
|
this.promise = promise; |
|
this.resolve = bind(internalResolve, state); |
|
this.reject = bind(internalReject, state); |
|
}; |
|
|
|
newPromiseCapabilityModule.f = newPromiseCapability = function (C) { |
|
return C === PromiseConstructor || C === PromiseWrapper |
|
? new OwnPromiseCapability(C) |
|
: newGenericPromiseCapability(C); |
|
}; |
|
|
|
if (!IS_PURE && isCallable(NativePromiseConstructor) && NativePromisePrototype !== Object.prototype) { |
|
nativeThen = NativePromisePrototype.then; |
|
|
|
if (!NATIVE_PROMISE_SUBCLASSING) { |
|
// make `Promise#then` return a polyfilled `Promise` for native promise-based APIs |
|
defineBuiltIn(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) { |
|
var that = this; |
|
return new PromiseConstructor(function (resolve, reject) { |
|
call(nativeThen, that, resolve, reject); |
|
}).then(onFulfilled, onRejected); |
|
// https://github.com/zloirock/core-js/issues/640 |
|
}, { unsafe: true }); |
|
} |
|
|
|
// make `.constructor === Promise` work for native promise-based APIs |
|
try { |
|
delete NativePromisePrototype.constructor; |
|
} catch (error) { /* empty */ } |
|
|
|
// make `instanceof Promise` work for native promise-based APIs |
|
if (setPrototypeOf) { |
|
setPrototypeOf(NativePromisePrototype, PromisePrototype); |
|
} |
|
} |
|
} |
|
|
|
$({ global: true, constructor: true, wrap: true, forced: FORCED_PROMISE_CONSTRUCTOR }, { |
|
Promise: PromiseConstructor |
|
}); |
|
|
|
setToStringTag(PromiseConstructor, PROMISE, false, true); |
|
setSpecies(PROMISE);
|
|
|