Angular RxJS retry
February 06, 2019
This page will walk through Angular RxJS retry
operator example. retry
operator returns source Observable
with the exception of an error. When source Observable
calls error then retry
operator resubscribe it for the maximum of given number of time. If Observable
starts emitting elements and suppose at any point it calls error before completion, then retry
operator will resubscribe the source Observable
and starts emitting from start again. Suppose we have used retry(3)
in our code, it means for any error in source Observable
, it will be resubscribed up to 3 times. If in first attempt of resubscribe by retry(3)
, it completes normally then no other attempt to resubscribe will be made.
RxJS
retry
operator is imported as following.
import { retry } from 'rxjs/operators';
retry
operator with examples step by step.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 7.0.0
2. Angular CLI 7.0.3
3. TypeScript 3.1.1
4. Node.js 10.3.0
5. NPM 6.1.0
6. RxJS 6.3.3
7. In-Memory Web API 0.6.1
Understanding RxJS retry
The RxJSretry
operator subscribes the source Observable
if it does not complete due to error. Now find the examples for the given scenarios.
Ex.1: Total max 3 retry, but no success and throw error.
For the demo suppose we have
of(1,2,3,4)
that will return Observable
instance and on subscribe it will emit 1,2,3,4. For the retry
demo we will throw error so that this Observable
instance does not complete successfully.
of(1,2,3,4).pipe( mergeMap(data => { if (data === 3) { return throwError('Error Occurred.'); } return of(data); }), retry(3) ).subscribe(res => console.log(res), err => { console.log("Retried for 3 times."); console.error(err); } );
1,2,1,2,1,2,1,2 Retried for 3 times. Error occurred.
retry
will try for 3 times maximum to get subscribe it successfully. If no success then finally error will be thrown. The Observable
instance will be subscribed 1+ 3 times totally. If in first retry only, source Observable
is subscribed with no error, then there will be no more retry.
Ex.2 : Total max 5 retry, but success in 3rd retry
let retryCount = 0; of('a','b','c','d').pipe( mergeMap(data => { if (data === 'c' && retryCount !== 3) { retryCount= retryCount + 1; return throwError('Error Occurred.'); } return of(data); }), retry(5) ).subscribe(res => console.log(res), err => { console.log("Number of retry: "+ retryCount); console.error(err); }, () => console.log("Processing Complete. Number of retry: "+ retryCount) );
a b a b a b a b c d Processing Complete. Number of retry: 3
In the above code for the first subscribe and retry 1 subscribe and retry 2 subscribe, there are errors. But in third retry, error is not thrown and
Observable
successfully subscribed and completed.
retry and catchError
Here we will create an example ofretry
with catchError
operator. catchError
handles the errors thrown by source Observable
. Using catchError
we can throw a default value or user defined error.
of("A", "B").pipe( switchMap(el => { if (el === "B") { throw new Error("Error occurred."); } return el; }), retry(2), catchError(err => { console.error(err.message); console.log("Error is handled"); return of("X"); //return defualt value as X }) ).subscribe(el => console.log(el), err => console.error(err), () => console.log("Processing Complete.") );
A A A Error occurred. Error is handled X
Observable
emits "B". We have passed retry
count 2. After all retry, we are still throwing error in our code. Once all the retry is complete then error is caught by catchError
operator. From catchError
we are throwing source Observable
with default element as "X".
retry with HttpClient
When we access data over HTTP, some errors can be handled by just retrying request such as if errors are transient and unlikely to repeat. For example if we have slow network error and our request could not become successful then there are the chances to make request successful if request is retried. Find the sample example.getBook(id: number): Observable<Book> { let url = this.bookUrl + "/" + id; return this.http.get<Book>(url).pipe( tap(() => console.log(url)), retry(3), // retry the failed request up to 3 times catchError(err => { console.log(err); return of(null); }) ); }
Complete Example
book.component.tsimport { Component, OnInit } from '@angular/core'; import { of, throwError } from 'rxjs'; import { switchMap, debounceTime, catchError, retry, mergeMap } from 'rxjs/operators'; import { BookService } from './book.service'; import { Book } from './book'; import { FormControl, FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-book', template: ` <h3>Search Book</h3> <form [formGroup]="bookForm"> ID: <input formControlName="bookId"> </form> <br/> <div *ngIf="book"> Id: {{book.id}}, Name: {{book.name}}, Category: {{book.category}} </div> ` }) export class BookComponent implements OnInit { book: Book; constructor(private bookService: BookService, private formBuilder: FormBuilder) { } ngOnInit() { //Total max 3 retry, but no success and throw error of(1,2,3,4).pipe( mergeMap(data => { if (data === 3) { return throwError('Error Occurred.'); } return of(data); }), retry(3) ).subscribe(res => console.log(res), err => { console.log("Retried for 3 times."); console.error(err); } ); //Total max 5 retry, but subscribe success in 2 retry let retryCount = 0; of('a','b','c','d').pipe( mergeMap(data => { if (data === 'c' && retryCount !== 3) { retryCount= retryCount + 1; return throwError('Error Occurred.'); } return of(data); }), retry(5) ).subscribe(res => console.log(res), err => { console.log("Number of retry: "+ retryCount); console.error(err); }, () => console.log("Processing Complete. Number of retry: "+ retryCount) ); //------------------------- this.retryAndHandleError(); this.searchBook(); } retryAndHandleError() { of("A", "B").pipe( switchMap(el => { if (el === "B") { throw new Error("Error occurred."); } return el; }), retry(2), catchError(err => { console.error(err.message); console.log("Error is handled"); return of("X"); //return defualt value as X }) ).subscribe(el => console.log(el), err => console.error(err), () => console.log("Processing Complete.") ); } bookId = new FormControl(); bookForm: FormGroup = this.formBuilder.group({ bookId: this.bookId } ); searchBook() { this.bookId.valueChanges.pipe( debounceTime(1000), switchMap(id => { return this.bookService.getBook(id); }) ).subscribe(res => this.book = res, err => console.log(err)); } }
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { Book } from './book'; import { catchError, retry, tap} from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class BookService { bookUrl = "/api/books"; constructor(private http: HttpClient) { } getBook(id: number): Observable<Book> { let url = this.bookUrl + "/" + id; return this.http.get<Book>(url).pipe( tap(() => console.log(url)), retry(3), // retry the failed request up to 3 times catchError(err => { console.log(err); return of(null); }) ); } }
export interface Book { id: number; name: string; category: string; }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-book></app-book> ` }) export class AppComponent { }
import { InMemoryDbService } from 'angular-in-memory-web-api'; export class TestData implements InMemoryDbService { createDb() { let bookDetails = [ { id: 101, name: 'Angular by Krishna', category: 'Angular' }, { id: 102, name: 'Core Java by Vishnu', category: 'Java' }, { id: 103, name: 'NgRx by Rama', category: 'Angular' } ]; return { books: bookDetails }; } }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { BookComponent } from './book.component'; //For InMemory testing import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { TestData } from './test-data'; @NgModule({ imports: [ BrowserModule, HttpClientModule, FormsModule, ReactiveFormsModule, InMemoryWebApiModule.forRoot(TestData) ], declarations: [ AppComponent, BookComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Install angular-in-memory-web-api@0.6.1
4. Run ng serve using command prompt.
5. Access the URL http://localhost:4200
We can see output in console as following.

References
RxJS retryThe RxJS library