Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions tests/retryAsync.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import retryAsync from '../utils/retryAsync.js';
import {
test,
assertEquals,
assertThrows,
printSummary
} from './test-utils.js';

// Test 1: should retry and succeed
test('should retry and succeed', async () => {
let count = 0;

const fn = async () => {
count++;
if (count < 3) {
throw new Error('fail');
}
return 'success';
};

const result = await retryAsync(fn, {
retries: 5,
delay: 100
});

assertEquals(result, 'success');
});

// Test 2: should throw error after max retries
test('should throw error after max retries', async () => {
const fn = async () => {
throw new Error('always fail');
};

try {
await retryAsync(fn, {
retries: 2,
delay: 100
});
} catch (error) {
assertEquals(error.message, 'always fail');
return;
}

throw new Error('Expected function to throw');
});

test('should throw error for invalid function', async () => {
try {
await retryAsync(null, { retries: 2 });
} catch (error) {
assertEquals(error.message, 'asyncFn must be a function');
return;
}

throw new Error('Expected function to throw');
});
// Print summary
printSummary();
59 changes: 59 additions & 0 deletions utils/retryAsync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Helper function to create delay
* @param {number} ms
* @returns {Promise<void>}
*/
const wait = (ms)=>{
return new Promise((resolve)=>{
setTimeout(resolve,ms);
})
}
/**
* retryAsync utility function
* @param {Function} asyncFn
* @param {Object} options
* @param {number} options.retries
* @param {number} options.delay
* @param {boolean} options.backoff
*/
const retryAsync = async (asyncFn, options = {}) => {
const {
retries = 3,
delay = 1000,
backoff = false
} = options;

// validation
if (typeof asyncFn !== 'function') {
throw new TypeError('asyncFn must be a function');
}

if (typeof retries !== 'number' || retries < 0) {
throw new TypeError('retries must be a non-negative number');
}

if (typeof delay !== 'number' || delay < 0) {
throw new TypeError('delay must be a non-negative number');
}

let attempt = 0;

while (attempt <= retries) {
try {
return await asyncFn();
} catch (error) {
if (attempt === retries) {
throw error;
}

const waitTime = backoff
? delay * Math.pow(2, attempt)
: delay;

await wait(waitTime);
attempt++;
}
}
};

export default retryAsync;