Last active
May 25, 2025 02:03
-
-
Save psenger/b19d967d1ddd1ec228a3a7704791b30a to your computer and use it in GitHub Desktop.
[Design Pattern: Cancelable Promise] #Promise #JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * This was generated by GPTChat, but it is 100% correct. | |
| * Wrap a Promise that can be canceled. | |
| * @type {CancelablePromise} | |
| */ | |
| module.exports = class CancelablePromise { | |
| /** | |
| * Creates a new CancelablePromise object. | |
| * @class | |
| * @constructor | |
| * @param {Promise} promise - A Promise to wrap. | |
| * @example | |
| * const promise = new Promise((resolve, reject) => { | |
| * setTimeout(() => { | |
| * resolve("Promise completed successfully"); | |
| * }, 1000); | |
| * }); | |
| * | |
| * const cancelablePromise = new CancelablePromise(promise); | |
| * | |
| * setTimeout(() => { | |
| * cancelablePromise.cancel(); | |
| * }, 500); | |
| * | |
| * cancelablePromise | |
| * .then((result) => { | |
| * console.log(result); // Will not be called, since the Promise was canceled | |
| * }) | |
| * .catch((error) => { | |
| * console.error(error); // Will log nothing, since the Promise was canceled | |
| * }); | |
| */ | |
| constructor( promise ) { | |
| /** | |
| * The wrapped Promise object. | |
| * @type {Promise} | |
| */ | |
| this.__promise = promise | |
| /** | |
| * Whether the Promise has been canceled. | |
| * @type {boolean} | |
| */ | |
| this.__isCanceled = false | |
| } | |
| /** | |
| * Cancels the Promise. | |
| */ | |
| cancel() { | |
| this.__isCanceled = true | |
| } | |
| /** | |
| * Attaches callbacks for the resolution and/or rejection of the Promise, and returns a new CancelablePromise object. | |
| * @param {function} onFulfilled - A function that is called when the Promise is fulfilled. If the Promise is already fulfilled, the function is called immediately. | |
| * @param {function} onRejected - A function that is called when the Promise is rejected. If the Promise is already rejected, the function is called immediately. | |
| * @returns {CancelablePromise} A new CancelablePromise object. | |
| */ | |
| then( onFulfilled, onRejected ) { | |
| const newPromise = this.__promise.then( | |
| ( value ) => { | |
| if ( !this.__isCanceled ) { | |
| return onFulfilled( value ) | |
| } | |
| }, | |
| ( reason ) => { | |
| if ( !this.__isCanceled ) { | |
| return onRejected( reason ) | |
| } | |
| } | |
| ) | |
| return new CancelablePromise( newPromise ) | |
| } | |
| /** | |
| * Attaches a callback for only the rejection of the Promise, and returns a new CancelablePromise object. | |
| * @param {function} onRejected - A function that is called when the Promise is rejected. If the Promise is already rejected, the function is called immediately. | |
| * @returns {CancelablePromise} A new CancelablePromise object. | |
| */ | |
| catch( onRejected ) { | |
| const newPromise = this.__promise.catch( ( reason ) => { | |
| if ( !this.__isCanceled ) { | |
| return onRejected( reason ) | |
| } | |
| } ) | |
| return new CancelablePromise( newPromise ) | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Wrap a Promise that can be canceled. | |
| * @type {CancelablePromiseWithCancelCallBack} | |
| */ | |
| module.exports = class CancelablePromiseWithCancelCallBack { | |
| /** | |
| * Creates a new CancelablePromise object. Should be noted it will not stop the promise from executing, rather. | |
| * @class | |
| * @constructor | |
| * @param {function|Promise} executor - A Promise to wrap or the execution of a promise to be wrapped. | |
| * @example | |
| * function createPromise(timeout) { | |
| * return new Promise((resolve, reject) => { | |
| * setTimeout(() => { | |
| * resolve('Hello, world!'); | |
| * }, timeout); | |
| * }); | |
| * } | |
| * | |
| * // create a new promise that resolves after 2 seconds | |
| * const myPromise = createPromise(2000); | |
| * | |
| * // create a new CancelablePromise that wraps the original promise | |
| * const myCancelablePromise = new CancelablePromise(myPromise); | |
| * | |
| * // cancel the promise after 1 second | |
| * setTimeout(() => { | |
| * myCancelablePromise.cancel(); | |
| * }, 1000); | |
| * | |
| * // attach a handler to the promise that will be called when it is resolved | |
| * myCancelablePromise.then(result => { | |
| * console.log(result); // this will not be called because the promise was canceled | |
| * }).catch(error => { | |
| * console.error(error); // this will not be called because the promise was canceled | |
| * }); | |
| * | |
| * // add a cancel handler that will be called when the promise is canceled | |
| * myCancelablePromise.onCancel(() => { | |
| * console.log('Promise canceled!'); | |
| * }); | |
| * | |
| * // create another promise that rejects after 3 seconds | |
| * const anotherPromise = new Promise((resolve, reject) => { | |
| * setTimeout(() => { | |
| * reject(new Error('Something went wrong!')); | |
| * }, 3000); | |
| * }); | |
| * | |
| * // create another CancelablePromise that wraps the original promise and allows up to 3 retries with a 1-second interval between them | |
| * const myRetryablePromise = new RetryablePromise(anotherPromise, { | |
| * retries: 3, | |
| * interval: 1000 | |
| * }); | |
| * | |
| * // attach a handler to the promise that will be called when it is resolved | |
| * myRetryablePromise.then(result => { | |
| * console.log(result); // this will not be called because the promise was rejected | |
| * }).catch(error => { | |
| * console.error(error); // this will be called after the third retry fails | |
| * }); | |
| * | |
| * // add a cancel handler that will be called when the promise is canceled | |
| * myRetryablePromise.onCancel(() => { | |
| * console.log('Promise canceled!'); | |
| * }); | |
| */ | |
| constructor( executor ) { | |
| /** | |
| * The wrapped Promise object. | |
| * @type {Promise} | |
| */ | |
| this.__promise = executor | |
| if ( !( executor instanceof Promise ) ) { | |
| this.__promise = new Promise( executor ) | |
| } | |
| /** | |
| * Whether the Promise has been canceled. | |
| * @type {boolean} | |
| */ | |
| this.__isCanceled = false | |
| /** | |
| * A function that is called when the promise is canceled. | |
| * @type {function} | |
| */ | |
| this.__cancelHandler = null | |
| } | |
| /** | |
| * Cancels the Promise. And call the cancelHandler if one is defined. | |
| */ | |
| cancel() { | |
| this.__isCanceled = true | |
| if (this.__cancelHandler) { | |
| this.__cancelHandler() | |
| } | |
| } | |
| /** | |
| * Attaches callbacks for the resolution and/or rejection of the Promise, and returns a new CancelablePromise object. | |
| * @param {function} onFulfilled - A function that is called when the Promise is fulfilled. If the Promise is already fulfilled, the function is called immediately. | |
| * @param {function} onRejected - A function that is called when the Promise is rejected. If the Promise is already rejected, the function is called immediately. | |
| * @returns {Promise} A Promise for the completion of which every callback is executed. | |
| */ | |
| then( onFulfilled, onRejected ) { | |
| if (this.__isCanceled) { | |
| return Promise.resolve() | |
| } | |
| return this.__promise.then(onFulfilled, onRejected) | |
| } | |
| /** | |
| * Attaches a callback for only the rejection of the Promise, and returns a new CancelablePromise object. | |
| * @param {function} onRejected - A function that is called when the Promise is rejected. If the Promise is already rejected, the function is called immediately. | |
| * @returns {CancelablePromise} A new CancelablePromise object. | |
| */ | |
| catch( onRejected ) { | |
| if (this.__isCanceled) { | |
| return Promise.resolve() | |
| } | |
| return this.__promise.catch(onRejected) | |
| } | |
| /** | |
| * Attaches a callback that is called when the Promise is settled, regardless of whether it was fulfilled or rejected. | |
| * @param {function} onFinally - A Function called when the Promise is settled. | |
| * @returns {Promise} A Promise for the completion of the callback. | |
| */ | |
| finally( onFinally ) { | |
| if ( this.__isCanceled ) { | |
| return Promise.resolve() | |
| } | |
| return this.__promise.finally( onFinally ) | |
| } | |
| /** | |
| * Adds a function that is called when the promise is canceled. | |
| * @param {function} cancelHandler - A function that is called when the promise is canceled. | |
| */ | |
| onCancel( cancelHandler ) { | |
| this.__cancelHandler = cancelHandler | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment