sandedwich
sandedwich

Reputation: 11

Can the retry operator catch errors like the retryWhen operator in RxJS?

Question is based off Reactive Patterns with RxJS for Angular by Lamis Chebbi. Chapter 5: Error Handling. In the section covering 'retrying strategies'. My problem stems from an example used in the book featuring the soon to be deprecated retryWhen operator.

Here's the relevant observable logic, provided through a dedicated service observable.service.ts

export class ObservableService {

  observable$ = from(['1', '2', '3', 'Hello', '100']);

}

Here is the relevant code in the consuming component app.component.ts

  ngOnInit() { 


    this.observableService.observable$.pipe(
      map((value) => { if (isNaN(value as any)) { throw new Error } else return parseInt(value); }),
      retryWhen((errors) => { return errors.pipe(delayWhen(() => timer(5000))); }),
    ).subscribe({
      next: (value) => console.log('Value emitted', value),
      error: (error) => console.log('Error: ', error),
      complete: () => console.log('Stream completed'),
    });

 
  }
}

And the output which repeats every 5 seconds indefinitely

Value emitted 1
Value emitted 2
Value emitted 3

From my understanding, it loops because retryWhen catches map()'s errors and uses them as its notifier's event stream; which upon emitting resubscribes retryWhen to the source observable. map() will always throw in this example.

This is fine. The problem is attempting to use retry similarly. RxJS recommends using this operator over retryWhen moving forward, and in doing so I run into issues. Namely, I'm not sure if retry catches errors the same way.

Here is my attempt at producing the same output using retry in place of retryWhen in the consuming component app.component.ts

  ngOnInit() { 


    this.observableService.observable$.pipe(
      map((value) => { if (isNaN(value as any)) { throw new Error } else return parseInt(value); }),
      retry({ delay: (error) => { return error.pipe(delayWhen(() => timer(5000))); } }),
    ).subscribe({
      next: (value) => console.log('Value emitted', value),
      error: (error) => console.log('Coming from observer error handling function: ', error),
      complete: () => console.log('Stream completed'),
    }); 


  }

Which produces

Value emitted 1
Value emitted 2
Value emitted 3
Coming from observer error handling function:  TypeError: error.pipe is not a function
    at delay (app.component.ts:22:48)
    at retry.js:42:99
    at OperatorSubscriber._error (OperatorSubscriber.js:23:21)
    at OperatorSubscriber.error (Subscriber.js:40:18)
    at OperatorSubscriber._next (OperatorSubscriber.js:16:33)
    at OperatorSubscriber.next (Subscriber.js:31:18)
    at Observable._subscribe (innerFrom.js:51:24)
    at Observable._trySubscribe (Observable.js:37:25)
    at Observable.js:31:30
    at errorContext (errorContext.js:19:9)

My goal is to reproduce the output shown in the book. Insight on getting the retry logic's output to loop indefinitely would be appreciated. Background on what's going on behind the scenes would be a nice-to-have. My leading assumption is retry's not receiving map()'s errors. Thanks

Upvotes: 1

Views: 799

Answers (1)

Mrk Sef
Mrk Sef

Reputation: 8062

Easy answer:

Use

retry({ delay: 5000 }),

Here's the doc on the RetryConfig

retryWhen

retryWhen is passed a lambda whose parameter is observable stream of errors. This reifies errors as observable events, which is pretty much never useful and became a source of confusion unto itself. Pretty much no other operator works this way, though it's not too hard to get this behavior back.

Retry config

You can use RetryConfig's delay property to either set a delay in ms directly or you can return an observable time like before.

retry({ delay: _ => timer(5000) }),

Upvotes: 0

Related Questions