Skip to content

Instantly share code, notes, and snippets.

@hirenchauhan2
Created September 5, 2023 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hirenchauhan2/b6cf750a56fc0ae6bf76be7b3ff1e930 to your computer and use it in GitHub Desktop.
Save hirenchauhan2/b6cf750a56fc0ae6bf76be7b3ff1e930 to your computer and use it in GitHub Desktop.
Retry a promise
/**
* AbortError is a special error which is used for aborting the retry mechanism of the `retryPromise` function.
*/
export class AbortError extends Error {
constructor(message: string) {
super(message);
}
}
/**
* Add waiting time in execution
* @param {number} time Number of milliseconds to wait
*/
function waitFor(time: number) {
return new Promise((res) => {
setTimeout(res, time);
});
}
interface RetryConfig {
retries: number;
onFailedAttempt: (error: ErrorTracker) => void;
};
interface ErrorTracker {
attemptNumber: number;
retriesLeft: number;
originalError: any
}
/**
* Retry promise execution until it gives successful result under the given number of retries.
*
* @param input function to call and retry if any error occurs except for AbortError which will not allow to retry
* @param options config for retry options
*
*/
export async function retryPromise(input: () => Promise<unknown>, options: RetryConfig) {
options = Object.assign({ retries: 2 }, options);
const retries = options.retries;
const errorTracker: ErrorTracker = {
attemptNumber: 0,
retriesLeft: retries,
originalError: null,
};
for (let i = 0; i < retries; i++) {
try {
errorTracker.attemptNumber++;
const result = await input();
return result;
} catch (error) {
errorTracker.originalError = error;
// exit immediately if the the error is for aborting the further retries.
if (error instanceof AbortError) {
throw error;
}
errorTracker.retriesLeft--;
if (
options.onFailedAttempt &&
typeof options.onFailedAttempt === 'function'
) {
options.onFailedAttempt(errorTracker);
}
// If the last attempt failed, then no need to add wait
if (errorTracker.retriesLeft > 0) {
await waitFor(errorTracker.attemptNumber * 1000);
}
}
}
throw errorTracker.originalError;
};
/**
* Some function to get data from API or to do some async task
**/
async function getRandomData(shouldFail = false, shouldAbort = false) {
const timeout = Math.floor(Math.random() * 2000);
console.log('random time to pretend a network call or some time consument task', timeout);
await waitFor(timeout);
if (shouldFail) {
if (shouldAbort) {
throw new AbortError('Abortttt....!');
}
throw new Error('Some non-priority error here');
}
return Promise.resolve('Yay!!');
}
async function demo() {
try {
// should fail after 3 retries
const result = await retryPromise(() => getRandomData(true), {
retries: 3,
onFailedAttempt: (error) => {
console.log(
`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`,
error.originalError
);
},
});
console.log(result);
} catch (err) {
console.log('Caught the error', err);
}
}
demo();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment