Skip to content

Instantly share code, notes, and snippets.

@maradondt
Last active April 29, 2025 13:17
Show Gist options
  • Save maradondt/6a4181e4137532065c4c7e0e12bf3223 to your computer and use it in GitHub Desktop.
Save maradondt/6a4181e4137532065c4c7e0e12bf3223 to your computer and use it in GitHub Desktop.
helper fn to retry requests follow different strategies
import { sleep } from './utils';
export type RetryOptions = {
/**
* Maximum Retry Count (Following Circuit Breaker Strategy)
* @default 3
*/
retryCount?: number;
/** Prevent retry after link has expired */
linkTtlSec?: number | null;
/**
* Delay in ms between retries
* @default 100
*/
delayMs?: number;
/**
* Exponential Backoff: Increases the delay exponentially with each retry attempt, reducing the load on the system.
*/
exponentialMultiplier?: number;
/**
* Jitter: Adds randomness to the delay to prevent synchronized retries from multiple clients.
*/
jitter?: boolean;
/**
* A function to determine if a retry should occur based on the error.
* If provided, retries will only happen if `matcher(error)` returns `true`.
*/
matcher?: (error: unknown) => boolean;
};
/**
*
* @example ```ts
* retry(download, {
* retryCount: 5,
* linkTtlSec: ttlSec,
* delayMs: 200,
* exponentialMultiplier: 2,
* });
*
* ```
*/
export function retry<T>(cb: () => Promise<T>, options: RetryOptions = {}) {
const { retryCount = 3, linkTtlSec, matcher } = options;
let counter = 0;
const firstStart = performance.now();
const run = async () => {
try {
counter += 1;
return await cb();
} catch (e) {
const retryTime = performance.now();
const delay = getDelay(counter, options);
const delaySec = getDelay(counter, options) / 1000;
const diffFromFirstStartSec = (retryTime - firstStart) / 1000;
if (linkTtlSec && diffFromFirstStartSec + delaySec > linkTtlSec) {
throw e;
}
if (counter > retryCount) {
throw e;
}
if (matcher && !matcher(e)) {
throw e;
}
await sleep(delay);
return run();
}
};
return run();
}
function getDelay(count: number, { delayMs, exponentialMultiplier, jitter }: RetryOptions) {
let delay = delayMs ?? 100;
if (exponentialMultiplier && !jitter) {
delay = delay * count * exponentialMultiplier;
}
if (jitter && !exponentialMultiplier) {
delay = delay * Math.random();
}
return delay;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment