Search code examples
rxjsrxjs-pipeable-operators

RxJS- Conditionally using RetryWhen operator


I would like to devise a scalable error handling strategy for my Observable-based Firebase requests. To do so, I am trying to create a custom RxJS operator for each type of error (e.g. unauthenticated, internal, you name it). The operators would be stacked after the request as follows:

FirebaseRequestObservable.pipe(
...
handleUnauthenticatedError(),
handleInternalError(),
handleYouNameItError(),
...
)

This works fine if my error handling operators only constitute of a catchError operator, in which case its internal structure can simply be:

source.pipe(
catchError((err) => {
  if (err !== errorThisOperatorShouldHandle) {
   throw err 
   } 
  handleError
 }
))

So that the error trickles down to the next custom operator if it shouldn't be handled by that operator.

The problem comes in when my error handling logic for a certain error involves using retryWhen. I do not know how I could then have the operator conditionally employ retryWhen if it is the right error, and re-throw the error to the next operator if it isn't.


Solution

  • They're pretty similar. Here's how I might implement these two:

    Handle a specific error.

    function handleYouNameItError<T>(): MonoTypeOperatorFunction<T> {
    
      return catchError(err => {
        if(err !== youNameItError){
          return throwError(() => err);
        }else{
          // handleError
          return EMPTY;
        }
      });
    
    }
    

    Retry a specific error.

    function retryYouNameItAnotherError<T>(): MonoTypeOperatorFunction<T> {
    
      return retryWhen(err$ => err$.pipe(
        tap(err => {
          if(err !== youNameItAnotherError){
            throw err;
          }
        }),
        // Delay 1s between retries
        delay(1000),
        // Only retry 5 times
        take(5),
        // After 5 retries, throw a new error
        concatWith(throwError(() => 
          new YouNameItAnotherErrorFailedAfterRetries()
        ))
      ));
    
    }
    

    Use them as operators :)

    FirebaseRequestObservable.pipe(
      ...
      handleYouNameItError(),
      retryYouNameItAnotherError()
      ...
    );