Angular + RxJS - concatMap

By Arvind Rai, February 02, 2024
On this page we will learn using RxJS concatMap operator in our Angular standalone application.
1. concatMap projects each source value to an observable and wait for complete before sending next value. The output is merged in the order they are processed.
2. Find the operator construct from RxJS doc.
concatMap(project: (value: T,
   index: number) => O): OperatorFunction<T, ObservedValueOf<O>> 
project is a function that returns an observable for an item emitted by source.
3. concatMap maps each value of the source to an observable and then flattens all these inner observables.
4. Import concatMap operator from 'rxjs' as given below.
import { concatMap } from 'rxjs'; 

1. Using concatMap

In our simple code we have three elements in the source observable that is being emitted to concatMap sequentially. The inner observable passed to concatMap has two elements. These values are being mapped after 2 second wait.
of(10, 11, 12).pipe(
   concatMap(sl => of("a", "b").pipe(
      delay(2000),
      map(el => sl+ "-" +el)        
      )
   )
).subscribe(res => console.log(res)); 
Output
(2 second wait)
10-a
10-b
(2 second wait)
11-a
11-b
(2 second wait)
12-a
12-b 
Look into the output. For first source element, 10, concatMap is applying to given observable that is waiting for 2 second and then emit 'a' and 'b'. These elements are combined by map and finally concatMap returns 10-a and 10-b. As the operation for first source element, 10, the operation is complete, concatMap accepts next source element, 11. After 2 second concatMap will accept next source element, 12, and so on.
If we use mergeMap in the above code, wait for 2 second only at start and then all output will displayed at once in console. If we use switchMap in the above code, only the latest inner observable is returned and the output will be 12-a, 12-b. If we use exhaustMap in the above code, only the oldest inner observable is returned and the output will be 10-a, 10-b.

2. concatMap vs mergeMap

mergeMap maps each value to an observable and then flattens all these inner observable using mergeAll where as concatMap concats all inner observables using concatAll. The mergeAll and concatAll both flattens an observable-of-observables but concatAll puts one inner observable after the other.
Find the code with mergeMap.
of("x", "y").pipe(
   mergeMap(sl => of("1", "2").pipe(
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
(After two second)
x1
x2
y1
y2 
Find the code with concatMap.
of("x", "y").pipe(
   concatMap(sl => of("1", "2").pipe(
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
(After two second)
x1
x2
(After two second)
y1
y2 
Use mergeMap when we want the output of all inner observables in one go and use concatMap when we want the output of all inner observables one by one.

3. concatMap vs switchMap

switchMap maps each value to an observable and flattens all these inner observables using switchAll. If before completing previous observable, new inner observable starts to execute then switchMap keeps only latest inner observables and discards the previous one.
Find the code.
of("Puna", "Mumbai").pipe( // outer observable
   switchMap(sl => of("MH").pipe( // inner observable
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
Mumbai-MH 
In the above code, every inner observable takes 2 second to complete. The outer observable is emitting two elements to switchMap. First inner observable is for "Puna" source element and second inner observable is for "Mumbai" source element. Second inner observable will start executing before completion of first inner observable because inner observables are taking two seconds to complete. In this case switchMap will return only latest inner observable i.e. for "Mumbai" element.

Now find the code for concatMap that will return flattened observable of all inner observables.
of("Puna", "Mumbai").pipe(
   concatMap(sl => of("MH").pipe(
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
Puna-MH
Mumbai-MH 

4. concatMap vs exhaustMap

exhaustMap is the opposite of switchMap operator. exhaustMap uses exhaustAll to flatten all inner observables. When source observable emits more than one elements then exhaustMap creates inner observables for each source element. If before completion of an inner observable, a new observable starts executing then that new inner observable is discarded and old inner inner observable is kept.
Find the code.
of(101, 102).pipe(
   exhaustMap(sl => of("abc").pipe(
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
101-abc 
concatMap keeps all inner observables flattened.
of(101, 102).pipe(
   concatMap(sl => of("abc").pipe(
      delay(2000),
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
101-abc
102-abc 

5. concatMap vs map

map applies the given project function to each value emitted by the source observable. If we pass an inner observable to map and when we subscribe the source observable, we get output as inner observable that need to be subscribed further.
of("Mahesh", "Shiva").pipe(
   map(sl => of("201").pipe(
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res.subscribe(v=> console.log(v)))); 
Output
Mahesh-201
Shiva-201 
Find the code with concatMap.
of("Mahesh", "Shiva").pipe(
   concatMap(sl => of("201").pipe(
      map(el => sl + "-" + el)
   ))
).subscribe(res => console.log(res)); 
Output
Mahesh-201
Shiva-201 

6. concatMap Example with HTTP Requests

emp.component.ts
import { Component, OnInit } from '@angular/core';
import { EmpService } from './services/emp.service';
import { CommonModule } from '@angular/common';
import { Observable, concatMap, of } from 'rxjs';

@Component({
   selector: 'my-app',
   standalone: true,
   imports: [CommonModule],
   templateUrl: './emp.component.html'
})
export class EmpComponent implements OnInit {
   constructor(private empService: EmpService) {
   }
   result$!: Observable<any>;
   ngOnInit() {
      this.result$ = of(101, 102).pipe(
         concatMap(id => this.empService.findEmpById(id))
      );
   }
} 
emp.component.html
{{result$ | async | json}} 
emp.service.ts
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class EmpService {
    constructor(private http: HttpClient) { }

    findEmpById(id: number): Observable<any[]> {
        const httpParams = new HttpParams().set('id', id);
        return this.http.get<any[]>("/api/getEmp", {
            params: httpParams
        });
    }
} 
app.config.ts
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { TestData } from './test-data';
import { provideHttpClient } from '@angular/common/http';

export const APP_CONFIG: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    importProvidersFrom(InMemoryWebApiModule.forRoot(TestData, { delay: 2000 }))
  ]
}; 
test-data.ts
import { InMemoryDbService } from 'angular-in-memory-web-api';

export class TestData implements InMemoryDbService {
  createDb() {
    let empDetails = [
      { id: '101', name: 'Mahesh' },
      { id: '102', name: 'Krishn' }
    ];
    return { getEmp: empDetails };
  }
} 
In our example we have a HTTP GET request to get employee data for given id. I am using InMemoryWebApi for the demo application that will respond request in 2 seconds. There are two employee data in our dummy db. Outer observable will emit two employee ids sequentially. For each id, concatMap will create inner observable. We are fetching employee data observable in HTML template using async pipe. When we run the application, we will see output for first employee after two seconds and then again after two seconds, output will be overridden by the second employee data.
Find the print-screen of the output.
Angular + RxJS – concatMap

7. References

8. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us