Skip to content

Instantly share code, notes, and snippets.

@lihaibh
Last active February 24, 2023 13:39
Show Gist options
  • Save lihaibh/7b9822f5e2a3d33b61d5619e4f68c719 to your computer and use it in GitHub Desktop.
Save lihaibh/7b9822f5e2a3d33b61d5619e4f68c719 to your computer and use it in GitHub Desktop.
An elegant way of performing exponential backoff retries on an async operation, using rxjs streams
import { Type } from '@nestjs/common';
import { defer, lastValueFrom, switchMapTo, throwError, timer } from 'rxjs';
import { catchError } from 'rxjs/operators';
export function withRetry<
T,
F extends (...args: any[]) => Promise<T>,
>(options: { retries: number; exceptErrors?: Type<Error>[]; fn: F }): F {
return (async (...args) => {
let remainRetries = options.retries || 0;
const out$ = defer(async () => {
return options.fn(...args);
});
const try$ = out$.pipe(
catchError((err) => {
remainRetries--;
const attemptCount = options.retries - remainRetries;
// retry when there are remaining retries todo depends on the error type
if (
remainRetries >= 0 &&
!options.exceptErrors?.find((type) => err instanceof type)
) {
return timer((attemptCount ^ 2) * 1000).pipe(switchMapTo(try$));
}
return throwError(() => err);
}),
);
return lastValueFrom(try$);
}) as F;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment