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
Easy answer:
Use
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.