Skip to content

Commit

Permalink
Add retry function and its test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewzolotukhin committed Feb 9, 2024
1 parent f8b0940 commit b271df5
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 1 deletion.
3 changes: 2 additions & 1 deletion libs/async/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Collector from './Collector.js';
import { retry } from './retry.js';

export { Collector };
export { Collector, retry };
58 changes: 58 additions & 0 deletions libs/async/src/retry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { retry } from './retry.js';

test('retry - 1', async () => {
let numCalled = 0;
const fn = () =>
new Promise((resolve, reject) => {
numCalled++;
if (numCalled < 2) {
reject('error');
return;
}
resolve('success');
});

const start = performance.now();
await expect(retry(fn)).resolves.toBe('success');
const end = performance.now();
expect(end - start).toBeGreaterThan(100);
expect(numCalled).toBe(2);
});

test('retry - 2', async () => {
let numCalled = 0;
const fn = () =>
new Promise((resolve, reject) => {
numCalled++;
if (numCalled < 4) {
reject('error');
return;
}
resolve('success');
});

const start = performance.now();
await expect(
retry(fn, { maxRetries: 5, minDelay: 10, delayFactor: 1 })
).resolves.toBe('success');
const end = performance.now();
expect(end - start).toBeGreaterThan(30);
expect(numCalled).toBe(4);
});

test('retry - 3', async () => {
let numCalled = 0;
const fn = () =>
new Promise(() => {
numCalled++;
throw new Error('error');
});

const start = performance.now();
await expect(
retry(fn, { maxRetries: 5, minDelay: 10, delayFactor: 2 })
).rejects.toThrow('error');
const end = performance.now();
expect(end - start).toBeGreaterThan(10 + 20 + 40 + 80 + 160);
expect(numCalled).toBe(6);
});
48 changes: 48 additions & 0 deletions libs/async/src/retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export type RetryOptions = {
/**
* Maximum number of retries. Defaults to 3.
*/
maxRetries: number;
/**
* Minimum delay in milliseconds between retries. Defaults to 100 (ms).
*/
minDelay?: number;
/**
* Factor by which to multiply the delay after each retry. Defaults to 2.
*/
delayFactor?: number;
};

/**
* Retry a async function up to `maxRetries` times with exponential backoff.
*/
export const retry = <T>(
/**
* The async function to retry.
*/
fn: () => Promise<T>,
/**
* Options.
*/
options?: RetryOptions
): Promise<T> => {
// retry `promise` up to `maxRetries` times with exponential backoff
const { maxRetries = 3, minDelay = 100, delayFactor = 2 } = options ?? {};
return new Promise((resolve, reject) => {
let retries = 0;
const tryPromise = () => {
fn()
.then(resolve)
.catch((error) => {
if (retries < maxRetries) {
retries++;
const delay = minDelay * Math.pow(delayFactor, retries);
setTimeout(tryPromise, delay);
} else {
reject(error);
}
});
};
tryPromise();
});
};

0 comments on commit b271df5

Please sign in to comment.