Retry Operator
retry()
Operator
The rety()
operator accepts an optional number of retries, and when the source Observable emits an error, the operator returns a clone of the source Observable excluding the error notification.
As you can imagine, internally, the retry()
operator tracks the number of retries, and when reached, will return the source Observable with the error notification.
A word of caution, the retry
operator works with both hot and cold Observables, but may lead to some confusion when using a hot
observable.
This is because the "retry" will not occur until the hot source Observable emits a next notification.
Example
Let's take a look an example of using the retry()
operator.
In this example we'll retry a failed Observable twice:
import { Subject, throwError, defer } from 'rxjs';
import { catchError, finalize, retry, tap } from 'rxjs/operators';
const subject = new Subject<void>();
defer(() => {
console.log('defer');
return subject;
})
.pipe(
tap(() => {
throw new Error('Error emitted by throw');
}),
catchError((error) => {
console.error('catchError', error);
return throwError(error);
}),
retry(2),
finalize(() => console.log('finalize'))
)
.subscribe({
error: (e) => console.error('observer', e),
next: (value) => console.log('next', value),
complete: () => console.log('complete')
});
subject.next();
subject.next();
subject.next();
Let's break this down:
- First, we create a new
Subject
whose generic type isvoid
. - Then, we'll use the
defer()
operator in order to perform a side effect upon subscribing. Thedefer()
operator accepts a function that is invoked every time an Observable is subscribed to, and returns the Observable. In this example, we'll log the "defer" message to the console and return thesubject
. - Immediately in the
tap()
operator we throw a newError
. - The
catchError()
operator's source Observable has emitted an error. So, thecatchError()
selector function is invoked with theerror
value provided as the first argument. Within the selector function we'll use theconsole.error()
method to log the error to the console, and then we return the Observable returned from thethrowError()
operator, providing theerror
value. - Now we use the
retry()
operator and specify2
for thecount
. This means that the retry operator will retry the source Observable that emits an error notification two (additional) times. - For some additional fun, we'll also use the
finalize()
operator to log out the messagefinalize()
when the source Observable has emitted an error notification, which will be a result of the two retries. - Then, we subscribe to the Observable and provide an Observer.
- Finally, note that we are emitting three next notifications on the
subject
instance.
What should the logging look like in the console?
Here is what the console should log:
- defer
- catchError Error: Error emitted by throw
- defer
- catchError Error: Error emitted by throw
- defer
- catchError Error: Error emitted by throw
- observer Error: Error emitted by throw
- finalize
This may be a bit confusing, so let's explain each log to the console in sequence:
- The Subject is subscribed to via the
subscribe()
method, and remember that we are logging "defer" each time the source Observable is subscribed to. - The
catchError()
operator catches the first error when we emit the first next notification and thetap()
operator throws an error. - The
retry()
operator receives the Observable from thethrowError()
operator that immediately emits an error notification. As such, we'll retry the first time. Internally, thecount
is decremented to1
. Theretry()
operator subscribes to a mirror of the source Observable without the error notification. Therefore, the "defer" message is logged to the console. - Round we go.
The
catchError()
operator receives the error notification again from the previoustap()
operator. - We retry for the second time.
Internally, the
count
is decremented to0
. Therefore, the "defer" message is logged to the console. - Round we go.
The
catchError()
operator receives the error notification again from the previoustap()
operator. Internally, thecount
is decremented to-1
. As such, we're done retrying. - With the error being rethrown and the retries extinguished, the
error()
function on the Observer is invoked with theerror
value. - Finally, the
finalize()
operator invokes the callback method and logs the message "finalize" to the console.