Understanding retryWhen
Like a standard observable, retryWhen forwards values from the source to the observer, acting as a mirror, until the source stream encounters an error. At that point, the operator invokes a callback function, providing it with an observable that emits error notifications, beginning with the current one. This callback runs exactly once, no matter how many errors occur or how many retries follow.
The callback must return an observable that serves as a signal for when the operator should re-subscribe to the original source. The operator subscribes to this guiding observable a single time and keeps that subscription active even as new errors surface or until the original source finishes. If the guiding observable emits a complete or error notification, retryWhen forwards that same notification to the observer. Any other value from the guiding observable prompts the operator to re-subscribe to the original source.
Here’s how retryWhen behaves:
- Subscribe to the source observable
- Pass each incoming value from the source to the observer
- If the source errors, run the callback and subscribe to the returned guiding observable
- Upon a new value from the guiding observable, re-subscribe to the original source
- If the guiding observable completes or errors, relay that complete or error notification to the observer
- When the source itself completes, send the complete notification to the observer
Practical Use
Failures are inevitable in software, and RxJS is no different. A network call, for instance, might fail for a variety of reasons. Having a way to retry an operation is often essential.
For straightforward retries on network requests, the retry operator is the better fit. However, when the retry decision hinges on conditions—like the nature of the error—retryWhen is the tool for the job.
The example below demonstrates using retryWhen to retry a request based on details from the error:
const url = 'https://i.imgur.com/fHyEMsl.jpg';
fromFetch(url).pipe(
switchMap((response) => response.json()),
retryWhen((errors) => {
return errors.pipe(
takeWhile((error) => {
if (error instanceof SyntaxError) {
throw new Error(error.message);
}
return true;
})
);
}
)
).subscribe();
