Last active
April 17, 2023 14:21
-
-
Save afruzan/8f21ab03b1deec2b74b562ed1a30486b to your computer and use it in GitHub Desktop.
JavaScript promise retry policy and delay with cancellation token
This file contains 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
export class RetryPolicy<T> { | |
constructor( | |
private action: () => Promise<T>, | |
private retryDelays: number[]) { | |
} | |
private retryNumber; | |
private cancellationToken: CancellationToken; | |
public async run(): Promise<T> { | |
this.retryNumber = -1; | |
this.cancellationToken = new CancellationToken(); | |
return this.run_internal(this.cancellationToken); | |
} | |
public cancel() { | |
this.cancellationToken.cancel(); | |
} | |
private async run_internal(cancellationToken: CancellationToken): Promise<T> { | |
try { | |
this.retryNumber += 1; | |
return await this.action(); | |
} catch (error) { | |
if (this.retryNumber <= this.retryDelays.length) { | |
if (cancellationToken.isCanceled) { | |
throw new RetryCanceledError(error, 'more retries canceled for the action.'); | |
} | |
try { | |
const ms = this.retryDelays[this.retryNumber]; | |
console.warn('action failed. waiting for ' + ms + ' ms before retrying...', error); | |
await delay(ms + 1, cancellationToken); | |
} catch (error) { | |
if (error instanceof PromiseCanceledError) { | |
throw new RetryCanceledError(error, 'more retries canceled for the action.'); | |
} | |
else { | |
throw error; | |
} | |
} | |
if (cancellationToken.isCanceled) { | |
throw new RetryCanceledError(error, 'more retries canceled for the action.'); | |
} | |
console.log('retrying the action...'); | |
return this.run_internal(cancellationToken); | |
} | |
else { | |
throw error; | |
} | |
} | |
} | |
} | |
export class RetryCanceledError extends Error { | |
constructor(public error: Error, message?: string) { | |
super(message) | |
} | |
} | |
function delay(ms, cancellationToken?: CancellationToken): Promise<void> { | |
return new Promise<void>((resolve, reject) => { | |
const t = setTimeout(() => resolve(), ms); | |
if (cancellationToken) { | |
cancellationToken.cancel = () => { | |
cancellationToken.isCanceled = true; | |
clearTimeout(t); | |
reject(new PromiseCanceledError('delay cancelled.')); | |
}; | |
} | |
}); | |
} | |
export class CancellationToken { | |
isCanceled: boolean = false; | |
cancel() { | |
this.isCanceled = true; | |
}; | |
} | |
export class PromiseCanceledError extends Error { | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment