(
fn: () => Promise<T>,
options: IRetryOptions = {}
)
| 176 | * the first attempt so the caller can react quickly. |
| 177 | */ |
| 178 | export const retryWithBackoff = async <T>( |
| 179 | fn: () => Promise<T>, |
| 180 | options: IRetryOptions = {} |
| 181 | ): Promise<T> => { |
| 182 | const config: IResolvedRetryOptions = { ...RETRY_DEFAULTS, ...options }; |
| 183 | let lastError: unknown; |
| 184 | |
| 185 | for (let attempt = 0; attempt <= config.maxRetries; attempt++) { |
| 186 | try { |
| 187 | return await fn(); |
| 188 | } catch (error: unknown) { |
| 189 | lastError = error; |
| 190 | |
| 191 | if ( |
| 192 | attempt === config.maxRetries || |
| 193 | !isRetryableError(error, config.retryableErrorTypes) |
| 194 | ) { |
| 195 | throw error; |
| 196 | } |
| 197 | |
| 198 | const delayMs = config.retryDelayMs * Math.pow(2, attempt); |
| 199 | |
| 200 | await delay(delayMs); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | throw lastError; |
| 205 | }; |
| 206 | |
| 207 | /* |
| 208 | * --------------------------------------------------------------------------- |
no test coverage detected