Created
September 6, 2025 10:04
-
-
Save mimshins/43a0e495054b46e6db7e8b621edac242 to your computer and use it in GitHub Desktop.
Executes an asynchronous action and retries it with an exponential backoff and jitter strategy if it fails.
This file contains hidden or 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
export type WithRetryOptions = { | |
/** | |
* The maximum number of retry attempts. | |
* Defaults to 5. | |
* | |
* @default 5 | |
*/ | |
maxRetries?: number; | |
/** | |
* The initial backoff time in milliseconds. | |
* Defaults to 100ms. | |
* | |
* @default 100 | |
*/ | |
initialDelay?: number; | |
/** | |
* The maximum backoff time in milliseconds. | |
* Defaults to 10000ms. | |
* | |
* @default 10000 | |
*/ | |
maxDelay?: number; | |
}; | |
/** | |
* Executes an asynchronous action and retries it with an exponential backoff and jitter strategy if it fails. | |
* This is useful for handling temporary errors like network issues or transient server failures. | |
* | |
* @param action The asynchronous function to execute and potentially retry. | |
* @param options Optional configuration for the retry behavior. | |
* | |
* @returns A promise that resolves with the result of the action on success, or null if all retry attempts fail. | |
*/ | |
export const withRetry = async <T>( | |
action: () => Promise<T>, | |
options: WithRetryOptions, | |
) => { | |
const { | |
initialDelay = 100, | |
maxDelay = 10000, | |
maxRetries = 5, | |
} = options ?? {}; | |
let attempts = 0; | |
let lastErr = null; | |
while (attempts < maxRetries) { | |
try { | |
return await action(); | |
} catch (err) { | |
attempts++; | |
lastErr = err; | |
if (attempts >= maxRetries) { | |
throw lastErr; | |
} | |
// Exponential backoff calculation | |
const baseDelay = initialDelay * Math.pow(2, attempts - 1); | |
// Add jitter to prevent a thundering herd problem | |
const jitter = Math.random() * baseDelay; | |
// Calculate final delay, capping at maxDelay | |
const delay = Math.min(baseDelay + jitter, maxDelay); | |
// eslint-disable-next-line no-console | |
console.warn( | |
`Attempt ${attempts} failed. Retrying in ${delay.toFixed(2)}ms...`, | |
); | |
await new Promise(resolve => { | |
setTimeout(resolve, delay); | |
}); | |
} | |
} | |
// This part of the code should be unreachable, as an error is thrown once `maxRetries` is reached. | |
// It is included as a fallback. | |
// eslint-disable-next-line no-console | |
console.warn(`This should be unreachable. Something went wrong!`); | |
return null; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment