Skip to content

Instantly share code, notes, and snippets.

@ca057
Created February 8, 2018 15:02
Show Gist options
  • Save ca057/109087e6bcd5fb42894bb01993c039ec to your computer and use it in GitHub Desktop.
Save ca057/109087e6bcd5fb42894bb01993c039ec to your computer and use it in GitHub Desktop.
Asynchronously retry a given task until it resolves or the maximum amount of retries is reached
/**
* Asynchronously retry a given task the given times, but as least as long as the task does not
* throw an error or returns the first time. The return value is a promise with the value
* of the rejected/resolved task.
*/
export const retryAsync = (times, task) => {
if (!task || typeof task !== 'function' || times < 0) {
throw new TypeError(
'No task passed or task is not of type function or times is a negative value.'
);
}
return new Promise(async (resolve, reject) => {
for (let i = 0; i < times; i++) {
try {
const result = await task();
resolve(result);
break;
} catch (error) {
if (i === times - 1) {
reject(error);
}
}
}
});
};
import { retryAsync } from './retryAsync'
describe('retryAsync()', () => {
it('should throw an error for invalid parameters', () => {
expect(retryAsync).toThrow(TypeError);
expect(() => retryAsync(-1)).toThrow(TypeError);
expect(() => retryAsync(1, 'no function')).toThrow(TypeError);
expect(() => retryAsync(-1, () => {})).toThrow(TypeError);
});
it('should call the task the specified times when it was not successful', async done => {
const mockErrorTask = jest.fn(() => {
throw new Error('');
});
const mockRejectedTask = jest.fn(() => Promise.reject());
const retries = 5;
try {
await retryAsync(retries, mockErrorTask);
} catch (error) {
// do not handle
}
try {
await retryAsync(retries, mockRejectedTask);
} catch (error) {
// do not handle
}
expect(mockErrorTask).toHaveBeenCalledTimes(retries);
expect(mockRejectedTask).toHaveBeenCalledTimes(retries);
done();
});
it('should resolve with the value of the task', async done => {
const expectedResult = 'Awesome result.';
const mockTask = jest.fn(() => Promise.resolve(expectedResult));
const result = await retryAsync(10, mockTask);
expect(result).toBe(expectedResult);
done();
});
it('should resolve with the value of the task if resolved after some retries', async done => {
const expectedResult = 'Awesome result.';
let counter = 0;
const mockTaskAfterThreeRuns = jest.fn(() => {
if (counter !== 2) {
counter++;
return Promise.reject();
}
return Promise.resolve(expectedResult);
});
const result = await retryAsync(5, mockTaskAfterThreeRuns);
expect(result).toBe(expectedResult);
expect(mockTaskAfterThreeRuns).toHaveBeenCalledTimes(3);
done();
});
it('should reject with the value of the task or error', async done => {
const expectedResult = 'Not so awesome error.';
const mockRejectedTask = jest.fn(() => Promise.reject(expectedResult));
const mockErrorTask = jest.fn(() => {
throw new Error(expectedResult);
});
expect(retryAsync(10, mockRejectedTask)).rejects.toThrow(expectedResult);
expect(retryAsync(10, mockErrorTask)).rejects.toThrow(expectedResult);
done();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment