HttpClient
and use it to get data from the backend. import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {EMPTY, Observable} from 'rxjs'; import {catchError} from 'rxjs/operators'; @Injectable() export class GreetingService { private GREET_ENDPOINT = 'http://localhost:3000'; constructor(private httpClient: HttpClient) { } greet(): Observable<string> { return this.httpClient.get<string>(`${this.GREET_ENDPOINT}/greet`).pipe( catchError(() => { // return EMPTY; }) ); } }
HttpClient
module and perform a simple GET request. If the request returns an error, we execute some code to process it and return an empty Observable
(observable object) in order to inform about it what initiated the request. This code as if says: "An error has occurred, but everything is in order, I will cope with it."/greet
not available or returns an error? Maybe there is a suitable RxJS operator? Of course he exists. RxJS has operators for just about anything.retry
operator. Let's look at its definition: “Returns an Observable that plays the original Observable with the exception of error
. If the source Observable calls error
, then this method, instead of propagating the error, will re-subscribe to the source Observable.count
(this is a numeric parameter passed to the method). "retry
operator retry
very similar to what we need. So let's embed it in our chain. import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {EMPTY, Observable} from 'rxjs'; import {catchError, retry, shareReplay} from 'rxjs/operators'; @Injectable() export class GreetingService { private GREET_ENDPOINT = 'http://localhost:3000'; constructor(private httpClient: HttpClient) { } greet(): Observable<string> { return this.httpClient.get<string>(`${this.GREET_ENDPOINT}/greet`).pipe( retry(3), catchError(() => { // return EMPTY; }), shareReplay() ); } }
retry
operator. Let's look at how this affected the behavior of the HTTP request that is executed in the experimental application. Here is a large GIF file that demonstrates the screen of this application and the Network tab of the browser’s developer tools. You will meet here a few more such demonstrations.PING THE SERVER
button.retry
operator solves the task assigned to it and repeats the execution of the failed request three times. The last attempt is successful, the application receives a response, a corresponding message appears on the page.retry
operator are not enough for us. Therefore, refer again to the RxJS documentation.retryWhen
, which seems to suit us. The documentation describes it as follows: “Returns an Observable that reproduces the original Observable, with the exception of error
. If the original Observable causes an error
, then this method will throw a Throwable that caused the error, Observable, returned from the notifier
. If this Observable calls complete
or error
, then this method will call complete
or error
on the child subscription. Otherwise, this method will re-subscribe to the original Observable. ”retryWhen
accepts a callback, which is returned by the Observable. The returned Observable decides how the retryWhen
operator retryWhen
based on some rules. Namely, this is how the retryWhen
operator retryWhen
:retryWhen
. retryWhen((errors: Observable<any>) => errors.pipe( delay(delayMs), mergeMap(error => retries-- > 0 ? of(error) : throwError(getErrorMessage(maxEntry)) )) )
retryWhen
operator is retryWhen
. In a callback, we have access to the error that caused the failure. We postpone errors
, reduce the number of retries, and return a new Observable, which gives an error.retryWhen
operator, this Observable, since it produces a value, performs a repeat request. If the repetition is several times unsuccessful and the value of the variable retries
reduced to 0, then we finish the work with an error that occurred during the execution of the request.retry
, which is in our chain. But here we will slow down a bit.retries
with variable retries
? This variable contains the current state of the system for retry failed requests. Where is she announced? When is the state reset? The state must be managed inside the stream, not outside it. const customOperator = (src: Observable<A>) => Observable<B>
delayMs
(delay between repetitions) and maxRetry
( maximum number of repetitions). const customOperator = (delayMs: number, maxRetry: number) => { return (src: Observable<A>) => Observable<B> }
Observable
class and implement the lift
function. 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; export function delayedRetry(delayMs: number, maxRetry = DEFAULT_MAX_RETRIES) { let retries = maxRetry; return (src: Observable<any>) => src.pipe( retryWhen((errors: Observable<any>) => errors.pipe( delay(delayMs), mergeMap(error => retries-- > 0 ? of(error) : throwError(getErrorMessage(maxRetry)) )) ) ); }
return this.httpClient.get<string>(`${this.GREET_ENDPOINT}/greet`).pipe( delayedRetry(1000, 3), catchError(error => { console.log(error); // return EMPTY; }), shareReplay() );
delayedRetry
operator in a chain and passed to it, as parameters, the numbers 1000 and 3. The first parameter specifies the delay in milliseconds between attempts to perform repeated requests. The second parameter determines the maximum number of repeated requests.retryWithBackoff
, that implements this behavior. 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_MAX_RETRIES, backoffMs = DEFAULT_BACKOFF) { let retries = maxRetry; return (src: Observable<any>) => src.pipe( retryWhen((errors: Observable<any>) => errors.pipe( mergeMap(error => { if (retries-- > 0) { const backoffTime = delayMs + (maxRetry - retries) * backoffMs; return of(error).pipe(delay(backoffTime)); } return throwError(getErrorMessage(maxRetry)); } )))); }
retry
not enough to organize a reliable system to repeat failed requests. The operator retryWhen
gives the developer a higher level of control over repeated requests. It allows you to customize the interval for the execution of repeated requests. Thanks to the capabilities of this operator, you can implement a scheme of pending repetitions or repetitions with exponential shelves.Source: https://habr.com/ru/post/459302/
All Articles