@effectful/cc
Version:
Multi-prompt delimited continuations runtime
124 lines (123 loc) • 4.51 kB
JavaScript
;
exports.__esModule = true;
exports.CancellationCallbacksSymbol = void 0;
exports.addOnCancel = addOnCancel;
exports.cancel = cancel;
exports.currentCancellationCallbacks = currentCancellationCallbacks;
exports.getCancellationCallbacks = getCancellationCallbacks;
exports.installCancelablePromise = installCancelablePromise;
exports.removeOnCancel = removeOnCancel;
exports.setCancellationCallbacks = setCancellationCallbacks;
exports.withCancellationCallbacks = withCancellationCallbacks;
const CancellationCallbacksSymbol = exports.CancellationCallbacksSymbol = Symbol.for("@effectful/cancelable/cancellationCallbacks");
const kInstalled = Symbol.for("@effectful/cancelable/installed");
let activeCancellationCallbacks;
function currentCancellationCallbacks() {
return activeCancellationCallbacks;
}
function addOnCancel(callback) {
const callbacks = activeCancellationCallbacks;
if (!callbacks) {
throw new Error("No active cancellation context");
}
callbacks.add(callback);
}
function removeOnCancel(callback) {
const callbacks = activeCancellationCallbacks;
if (!callbacks) {
throw new Error("No active cancellation context");
}
callbacks.delete(callback);
}
function withCancellationCallbacks(callbacks, body) {
const prev = activeCancellationCallbacks;
activeCancellationCallbacks = callbacks;
try {
return body();
} finally {
activeCancellationCallbacks = prev;
}
}
function getCancellationCallbacks(target) {
if (!target || typeof target !== "object" && typeof target !== "function") {
return undefined;
}
return target[CancellationCallbacksSymbol];
}
function setCancellationCallbacks(target, callbacks) {
if (!target || typeof target !== "object" && typeof target !== "function") {
return;
}
const obj = target;
if (obj[CancellationCallbacksSymbol] === callbacks) return;
Object.defineProperty(obj, CancellationCallbacksSymbol, {
value: callbacks,
configurable: true
});
}
function cancel(target) {
if (target instanceof Set) {
drain(target);
return;
}
const callbacks = getCancellationCallbacks(target);
if (callbacks) drain(callbacks);
}
function drain(callbacks) {
for (const cb of Array.from(callbacks)) {
callbacks.delete(cb);
try {
cb();
} catch (_e) {
// ignore cancellation errors
}
}
}
function wrapCombinator(PromiseImpl, name) {
const original = PromiseImpl[name];
if (typeof original !== "function") return;
PromiseImpl[name] = function patchedCombinator() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
const parentCallbacks = activeCancellationCallbacks;
const callCallbacks = new Set();
let parentDrain;
if (parentCallbacks) {
parentDrain = () => drain(callCallbacks);
parentCallbacks.add(parentDrain);
}
const base = withCancellationCallbacks(callCallbacks, () => original.apply(this, args));
setCancellationCallbacks(base, callCallbacks);
return base.then(value => {
if (parentCallbacks && parentDrain) parentCallbacks.delete(parentDrain);
drain(callCallbacks);
return value;
}, error => {
if (parentCallbacks && parentDrain) parentCallbacks.delete(parentDrain);
drain(callCallbacks);
throw error;
});
};
}
function installCancelablePromise(PromiseImpl) {
const BasePromise = PromiseImpl != null ? PromiseImpl : require("promise");
if (BasePromise[kInstalled]) return BasePromise;
Object.defineProperty(BasePromise, kInstalled, {
value: true
});
const originalThen = BasePromise.prototype.then;
BasePromise.prototype.then = function patchedThen(onFulfilled, onRejected) {
const callbacks = getCancellationCallbacks(this) || activeCancellationCallbacks;
const wrappedOnFulfilled = typeof onFulfilled === "function" && callbacks ? value => withCancellationCallbacks(callbacks, () => onFulfilled(value)) : onFulfilled;
const wrappedOnRejected = typeof onRejected === "function" && callbacks ? reason => withCancellationCallbacks(callbacks, () => onRejected(reason)) : onRejected;
const next = originalThen.call(this, wrappedOnFulfilled, wrappedOnRejected);
if (callbacks) setCancellationCallbacks(next, callbacks);
return next;
};
wrapCombinator(BasePromise, "all");
wrapCombinator(BasePromise, "race");
wrapCombinator(BasePromise, "any");
wrapCombinator(BasePromise, "allSettled");
return BasePromise;
}