From 37dffcfb6fb82f390fb5809da14228d43b07bf36 Mon Sep 17 00:00:00 2001 From: Sumanth Chinthagunta Date: Wed, 3 Jul 2019 15:55:15 -0700 Subject: [PATCH] feat(core): adding custom rxjs operator retryWithBackoff inspired by https://blog.angularindepth.com/retry-failed-http-requests-in-angular-f5959d486294 --- libs/core/src/lib/services/profile.service.ts | 14 ++++++--- libs/utils/src/index.ts | 1 + libs/utils/src/lib/retry-with-backoff.ts | 31 +++++++++++++++++++ package.json | 2 +- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 libs/utils/src/lib/retry-with-backoff.ts diff --git a/libs/core/src/lib/services/profile.service.ts b/libs/core/src/lib/services/profile.service.ts index 467bc45fd..20dc23dd8 100644 --- a/libs/core/src/lib/services/profile.service.ts +++ b/libs/core/src/lib/services/profile.service.ts @@ -3,7 +3,8 @@ import { Injectable } from '@angular/core'; import { Profile } from '@ngx-starter-kit/models'; import { environment } from '@env/environment'; import { catchError } from 'rxjs/operators'; -import { throwError } from 'rxjs'; +import { EMPTY, throwError } from 'rxjs'; +import { retryWithBackoff } from '@ngx-starter-kit/utils'; @Injectable({ providedIn: 'root', @@ -15,9 +16,14 @@ export class ProfileService { constructor(private httpClient: HttpClient) {} getMyProfile() { - return this.httpClient - .get(`${this.baseUrl}/${this.entityPath}/myprofile`) - .pipe(catchError(this.handleError)); + return this.httpClient.get(`${this.baseUrl}/${this.entityPath}/myprofile`).pipe( + retryWithBackoff(1000, 3), + catchError(error => { + this.handleError(error); + return EMPTY; + }), + // catchError(this.handleError) + ); } create(entity: Partial) { diff --git a/libs/utils/src/index.ts b/libs/utils/src/index.ts index 3b69e22f7..1fea229cf 100644 --- a/libs/utils/src/index.ts +++ b/libs/utils/src/index.ts @@ -3,6 +3,7 @@ export { waitUntil } from './lib/wait-until'; export { DeepPartial } from './lib/deep-partial'; export { ObjectLiteral } from './lib/object-literal'; export { delayAtLeast } from './lib/delay-at-least'; +export { retryWithBackoff } from './lib/retry-with-backoff'; export { toClass } from './lib/to-class'; export { fromAsyncIterator } from './lib/from-async-iterator'; export * from './lib/require-multi'; diff --git a/libs/utils/src/lib/retry-with-backoff.ts b/libs/utils/src/lib/retry-with-backoff.ts new file mode 100644 index 000000000..91f3779d0 --- /dev/null +++ b/libs/utils/src/lib/retry-with-backoff.ts @@ -0,0 +1,31 @@ +import { Observable, of, throwError } from 'rxjs'; +import { 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 = 5; +const DEFAULT_BACKOFF = 1000; + +export function retryWithBackoff( + delayMs: number, + maxRetry = DEFAULT_BACKOFF, + backoffMs = DEFAULT_BACKOFF, +): (src$: Observable) => Observable { + let retries = maxRetry; + + return (src$: Observable): Observable => + src$.pipe( + retryWhen((errors: Observable) => + errors.pipe( + mergeMap(error => { + if (retries-- > 0) { + const backoffTime = delayMs + (maxRetry - retries) * backoffMs; + return of(error).pipe(delay(backoffTime)); + } + return throwError(getErrorMessage(maxRetry)); + }), + ), + ), + ); +} diff --git a/package.json b/package.json index 5425df213..2bbf2edb3 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ }, "lint-staged": { "{apps,libs}/**/*.{ts,json,md,scss,html}": [ - "yarn affected:lint --uncommitted --parallel --fix", + "yarn affected:lint --uncommitted --parallel -- --fix", "yarn format:write --uncommitted", "git add" ]