Skip to content

Instantly share code, notes, and snippets.

@janpapenbrock
Forked from ikismail/album.service.ts
Created June 11, 2021 06:33
Show Gist options
  • Save janpapenbrock/f79ecce130be423c9efac63b7e371cae to your computer and use it in GitHub Desktop.
Save janpapenbrock/f79ecce130be423c9efac63b7e371cae to your computer and use it in GitHub Desktop.
Retry failed HTTP requests in Angular | Immediate retry, delayed retry and retry with backoff
/**
* 1. Immediate retry
* 2. delayed retry
* 3. retry with backoff
*/
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { EMPTY, Observable, of, throwError } from "rxjs";
import {
catchError,
retry,
shareReplay,
delay,
mergeMap,
retryWhen
} from "rxjs/operators";
const getErrorMessage = (maxRetry: number) =>
`Tried to load resource over XHR for ${maxRetry} times without success. Giving Up.`;
const DEFAULT_MAX_RETRIES: number = 5;
const DEFAULT_BACKOFF: number = 1000;
export function delayedRetry(delayMs: number, maxRetry = DEFAULT_MAX_RETRIES) {
let retries = maxRetry;
return (src: Observable<any>) =>
src.pipe(
retryWhen((err: Observable<any>) =>
err.pipe(
delay(delayMs),
mergeMap(error =>
retries-- > 0
? of(error)
: throwError(getErrorMessage(maxRetry))
)
)
)
);
}
export function retryWithBackOff(
delayMs: number,
maxRetry = DEFAULT_MAX_RETRIES,
backOffMs = DEFAULT_BACKOFF
) {
let retries = maxRetry;
return (src: Observable<any>) =>
src.pipe(
retryWhen((err: Observable<any>) =>
err.pipe(
mergeMap(error => {
if (retries-- > 0) {
const backOffTime =
delayMs + (maxRetry - retries) * backOffMs;
return of(error).pipe(delay(backOffTime));
}
return throwError(getErrorMessage(maxRetry));
})
)
)
);
}
@Injectable()
export class AlbumService {
private ALBUM_ENDPOINT = "http://localhost:3200";
constructor(private http: HttpClient) {}
// Usually, In Angular we create a service, Inject the HttpClient and use it to get data from the backend
albums(): Observable<any[]> {
return this.http.get<any[]>(`${this.ALBUM_ENDPOINT}/albums`).pipe(
catchError(() => {
// Perform some error handling
return EMPTY;
})
);
}
// 1. Using Immediate Retry
albums_immediate_retry(): Observable<any[]> {
return this.http.get<any[]>(`${this.ALBUM_ENDPOINT}/albums`).pipe(
retry(3),
catchError(() => {
// Perform some error handling
return EMPTY;
}),
shareReplay()
);
}
// 2. Using Delayed Retry
albums_with_delayed_retry(): Observable<any[]> {
return this.http.get<any[]>(`${this.ALBUM_ENDPOINT}/albums`).pipe(
delayedRetry(1000),
catchError(() => {
// Perform some error handling
return EMPTY;
}),
shareReplay()
);
}
// 3. retry with backoff
albums_with_retry_backoff(): Observable<any[]> {
return this.http.get<any[]>(`${this.ALBUM_ENDPOINT}/albums`).pipe(
retryWithBackOff(1000),
catchError(() => {
// Perform some error handling
return EMPTY;
}),
shareReplay()
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment