Created
February 8, 2018 15:02
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} | |
} | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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