blob: d96fc0ba95f313cba942d32d0939d704254241c9 [file] [log] [blame]
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {Scheduler, Task} from './scheduler';
export class RetryError<T> extends Error {
constructor(readonly payload: T, message = 'Retry Error') {
super(message);
}
}
function untilTimeout(ms: number) {
return new Promise(resolve => window.setTimeout(resolve, ms));
}
/**
* The scheduler that retries tasks on RetryError.
*
* The task is only retried if the RetryError was thrown, all other errors cause
* the worker to stop and the error is re-thrown.
*
* The number of retries are limited by maxRetry, the retries are performed
* according to exponential backoff, configured by backoffIntervalMs
* and backoffFactor.
*/
export class RetryScheduler<T> implements Scheduler<T> {
constructor(
private readonly base: Scheduler<T>,
private maxRetry: number,
private backoffIntervalMs: number,
private backoffFactor: number = 1.618
) {}
async schedule(task: Task<T>): Promise<T> {
let tries = 0;
let timeout = this.backoffIntervalMs;
const worker: Task<T> = async () => {
try {
return await this.base.schedule(task);
} catch (e: unknown) {
if (e instanceof RetryError && tries++ < this.maxRetry) {
await untilTimeout(timeout);
timeout = timeout * this.backoffFactor;
return await worker();
} else {
throw e;
}
}
};
return worker();
}
}