@techmely/utils
Version:
Collection of helpful JavaScript / TypeScript utils
116 lines (112 loc) • 3.04 kB
JavaScript
/*!
* @techmely/utils
* Copyright(c) 2021-2024 Techmely <techmely.creation@gmail.com>
* MIT Licensed
*/
// src/p.cancelable.ts
var CancelError = class extends Error {
constructor(reason) {
super(reason || "Promise was canceled");
this.name = "CancelError";
}
get isCanceled() {
return true;
}
};
var promiseState = Object.freeze({
pending: Symbol("pending"),
canceled: Symbol("canceled"),
resolved: Symbol("resolved"),
rejected: Symbol("rejected")
});
var PCancelable = class _PCancelable extends Promise {
static fn(userFunction) {
return (...arguments_) => new _PCancelable((resolve, reject, onCancel) => {
arguments_.push(onCancel);
userFunction(...arguments_).then(resolve, reject);
});
}
#cancelHandlers = [];
#rejectOnCancel = true;
#state = promiseState.pending;
#promise;
#reject = () => {
};
constructor(executor) {
super(executor);
this.#promise = new Promise((resolve, reject) => {
this.#reject = reject;
const onCancel = (handler) => {
if (this.#state !== promiseState.pending) {
throw new Error(
`The \`onCancel\` handler was attached after the promise ${this.#state.description}.`
);
}
this.#cancelHandlers.push(handler);
};
const addedProperties = {
shouldReject: {
get: () => this.#rejectOnCancel,
set: (boolean) => {
this.#rejectOnCancel = boolean;
}
}
};
objectDefProperties(onCancel, addedProperties);
const onResolve = (value) => {
if (this.#state !== promiseState.canceled || !onCancel.shouldReject) {
resolve(value);
this.#setState(promiseState.resolved);
}
};
const onReject = (error) => {
if (this.#state !== promiseState.canceled || !onCancel.shouldReject) {
reject(error);
this.#setState(promiseState.rejected);
}
};
executor(onResolve, onReject, onCancel);
});
}
// @ts-expect-error Ignore then
then(onFulfilled, onRejected) {
return this.#promise.then(onFulfilled, onRejected);
}
catch(onRejected) {
return this.#promise.catch(onRejected);
}
finally(onFinally) {
return this.#promise.finally(onFinally);
}
cancel(reason) {
if (this.#state !== promiseState.pending) {
return;
}
this.#setState(promiseState.canceled);
if (this.#cancelHandlers.length > 0) {
try {
for (const handler of this.#cancelHandlers) {
handler();
}
} catch (error) {
this.#reject(error);
return;
}
}
if (this.#rejectOnCancel) {
this.#reject(new CancelError(reason));
}
}
get isCanceled() {
return this.#state === promiseState.canceled;
}
#setState(state) {
if (this.#state === promiseState.pending) {
this.#state = state;
}
}
};
function objectDefProperties(obj, objAddendum) {
Object.defineProperties(obj, objAddendum);
}
export { PCancelable };