How to properly handle errors using retryWhen in rxjs

1k Views Asked by At

I need to retry a specific API call for about three times when there is a 502 bad gateway error. The retry may happen with slightly incremental delays. This is the code in the component.

some.component.ts

someMethod() {
    let attempt = 0;
    this.someService.AppendSubject(this.subject)
    .pipe(retryWhen(errors => errors.pipe(
        tap(error => this.retryApi(error, ++attempt, 'Server busy. Retrying request', this.subject))
    )))
    .subscribe(response => {
        //do something with response
    });
}

retryApi(error: any, attempt: number, retryMessage: string, data?: any) {
    delay(this.duration*attempt)
    //show toast with retryMessage
    if (attempt > this.retryLimit) {
        //show some message
        if (data)
            this.notifyFailure(data);
        throw error;
    }
}

notifyFailure(data: any) {
    this.someService.notifyFailure(data);
}

some.service.ts

public AppendSubject(data: any) {
    return this.http.post<any>(`{env.api}/appendsubject/`, data);
}    

public notifyFailure(data: any) {
    return this.http.post<any>(`{env.api}/notifyfailure/`, data);
}
  • The notifyFailure endpoint is not being hit at all at the server side.

Is this due to how I am doing/not doing the catching of errors in the retry? How do I properly catch errors in the above piece of code and not obstruct or kill the further notifyFailure call?

Note: I am not doing this in the interceptor currently since this is only for specific APIs at this point. I am writing the retry handler in the component and not at the some.service.ts level since I didn't know how to do that and while showing appropriate toasts and popups in the component.

2

There are 2 best solutions below

0
On

Good day! I prefer to use such approach:

Your service:

getSomething(retryCount: number = 0, retryInterval: number = 1000) {
    return this.http.get(this.getSomethingUrl).pipe(
      retryWhen((error: Observable<any>) => error.pipe(
        delay(retryInterval),
        take(retryCount)
      ))
    );
  }

In component:

this.service.getSomething(3, 2000).subscribe(response => {
  // Handle success
}, error => {
  // Here you can send a request to your BE (triggers only after 3 failed requests in this case), to notify about the occurred error
  // Also you can do it in your pipe using `catchError` rxjs operator
});
0
On

Based on AlexanderFSP's answer, I modified my code to catch errors this way.

.pipe(retryWhen(errors => errors.pipe(
    tap(error => this.retryApi(error, ++attempt, 'Server busy. Retrying request', this.subject)),
    catchError(() => {
        this.notifyFailure(data);
        return EMPTY;
    })
)))
.subscribe(response => {
    //do something with response
});

It is working for now.