Search code examples
javascriptangularrxjsrxjs6

RxJs retryWhen throwing exception before all retry attempt


I'm trying to implement an api call that will be

  1. retried few times if there is any errors
  2. after a specific time delay
  3. with some other condition check like - if returned success response json has any field null, I'll retry the api call

I tried to implement what is mentioned here - Rxjs Retry with Delay function

Following is my code segment

Api call

    delay = 5000;
    retryCount = 5;
    return this.httpClient.post(http://localhost:8080/info,JSON.stringify(data))
    .pipe(
      retryWhen(errors => 
          errors.pipe(
            delay(this.delay), 
            take(this.retryCount),
            tap(val => {
              console.log('Retrying.');
            }),
            concatMap(() => Observable.throw(new Error('Retry limit exceeded!')))
          )
        )
    );

Processing the response

this.searchService.searchInfo(param1, param2).subscribe(data => { this.handleSuccessResponse(data) }, (error) => {
        if (error) {
          // Handle specific error here
          handleErrorResponse(error);
        }
});


handleSuccessResponse(data){
  // handle success response here
}

handleErrorResponse(error){
  // Handle generic error here
}

The issue I am getting, before retrying for 5 times as I mentioned in the code, the exception in the concatMap is getting throwed. What I am missing here?

I'm using RxJS 6.4 with Angular12


Solution

  • I went through the learnRxjs docs and got this generic method, which seems to satisfy your requirement!

    import { Observable } from 'rxjs/Observable';
    import { _throw } from 'rxjs/observable/throw';
    import { timer } from 'rxjs/observable/timer';
    import { mergeMap, finalize } from 'rxjs/operators';
    
    export const genericRetryStrategy =
      ({
        maxRetryAttempts = 3,
        scalingDuration = 1000,
        excludedStatusCodes = [],
      }: {
        maxRetryAttempts?: number;
        scalingDuration?: number;
        excludedStatusCodes?: number[];
      } = {}) =>
      (attempts: Observable<any>) => {
        return attempts.pipe(
          mergeMap((error, i) => {
            const retryAttempt = i + 1;
            // if maximum number of retries have been met
            // or response is a status code we don't wish to retry, throw error
            if (
              retryAttempt > maxRetryAttempts ||
              excludedStatusCodes.find((e) => e === error.status)
            ) {
              return _throw('Retry limit exceeded!');
            }
            console.log(
              `Attempt ${retryAttempt}: retrying in ${
                retryAttempt * scalingDuration
              }ms`
            );
            // retry after 1s, 2s, etc...
            return timer(retryAttempt * scalingDuration);
          }),
          finalize(() => console.log('We are done!'))
        );
      };
    

    forked stackblitz