'use strict'; class CancelError extends Error { constructor(reason) { super(reason || 'Promise was canceled'); this.name = 'CancelError'; } get isCanceled() { return true; } } class PCancelable { static fn(userFn) { return (...arguments_) => { return new PCancelable((resolve, reject, onCancel) => { arguments_.push(onCancel); // eslint-disable-next-line promise/prefer-await-to-then userFn(...arguments_).then(resolve, reject); }); }; } constructor(executor) { this._cancelHandlers = []; this._isPending = true; this._isCanceled = false; this._rejectOnCancel = true; this._promise = new Promise((resolve, reject) => { this._reject = reject; const onResolve = value => { if (!this._isCanceled || !onCancel.shouldReject) { this._isPending = false; resolve(value); } }; const onReject = error => { this._isPending = false; reject(error); }; const onCancel = handler => { if (!this._isPending) { throw new Error('The `onCancel` handler was attached after the promise settled.'); } this._cancelHandlers.push(handler); }; Object.defineProperties(onCancel, { shouldReject: { get: () => this._rejectOnCancel, set: boolean => { this._rejectOnCancel = boolean; } } }); return executor(onResolve, onReject, onCancel); }); } then(onFulfilled, onRejected) { // eslint-disable-next-line promise/prefer-await-to-then return this._promise.then(onFulfilled, onRejected); } catch(onRejected) { return this._promise.catch(onRejected); } finally(onFinally) { return this._promise.finally(onFinally); } cancel(reason) { if (!this._isPending || this._isCanceled) { return; } this._isCanceled = true; 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._isCanceled; } } Object.setPrototypeOf(PCancelable.prototype, Promise.prototype); module.exports = PCancelable; module.exports.CancelError = CancelError;