diff --git a/tests/retryAsync.test.js b/tests/retryAsync.test.js new file mode 100644 index 0000000..eab171d --- /dev/null +++ b/tests/retryAsync.test.js @@ -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(); \ No newline at end of file diff --git a/utils/retryAsync.js b/utils/retryAsync.js new file mode 100644 index 0000000..5a9e2c6 --- /dev/null +++ b/utils/retryAsync.js @@ -0,0 +1,59 @@ +/** + * Helper function to create delay + * @param {number} ms + * @returns {Promise} + */ +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; \ No newline at end of file