UNPKG

backoff-rxjs

Version:

A collection of helpful RxJS operators to deal with backoff strategies (like exponential backoff)

64 lines (61 loc) 2.11 kB
import { defer, iif, Observable, throwError, timer, concatMap, retryWhen, tap } from 'rxjs'; import { exponentialBackoffDelay, getDelay } from '../utils'; export interface RetryBackoffConfig { // Initial interval. It will eventually go as high as maxInterval. initialInterval: number; // Maximum number of retry attempts. maxRetries?: number; // Maximum delay between retries. maxInterval?: number; // When set to `true` every successful emission will reset the delay and the // error count. resetOnSuccess?: boolean; // Conditional retry. shouldRetry?: (error: any) => boolean; backoffDelay?: (iteration: number, initialInterval: number) => number; } /** * Returns an Observable that mirrors the source Observable except with an error. * If the source Observable calls error, rather than propagating * the error call this method will resubscribe to the source Observable with * exponentially increasing interval and up to a maximum of count * re-subscriptions (if provided). Retrying can be cancelled at any point if * shouldRetry returns false. */ export function retryBackoff( config: number | RetryBackoffConfig ): <T>(source: Observable<T>) => Observable<T> { const { initialInterval, maxRetries = Infinity, maxInterval = Infinity, shouldRetry = () => true, resetOnSuccess = false, backoffDelay = exponentialBackoffDelay, } = typeof config === 'number' ? { initialInterval: config } : config; return <T>(source: Observable<T>) => defer(() => { let index = 0; return source.pipe( retryWhen<T>(errors => errors.pipe( concatMap(error => { const attempt = index++; return iif( () => attempt < maxRetries && shouldRetry(error), timer( getDelay(backoffDelay(attempt, initialInterval), maxInterval) ), throwError(error) ); }) ) ), tap(() => { if (resetOnSuccess) { index = 0; } }) ); }); }