In the world of web application, there is always a requirement you might face to make multiple Http requests.
It can be parallel or sequential, But In angular application, Http requests are by default observable, so we have several ways to handle it. Like using
- nested subscription
- mergeMap or concatMap or switchMap
- forkJoin
- Converting Http observable to promise
consider a scenario, We have blog application, In which we want author details of particular post and as request we have post id only, So in this scenario
first, we need to fetch post details, where we will get author id
second, On the basis of author id we will fetch author details
we will be using free json api for fetching data
https://jsonplaceholder.typicode.com/
Nested subscription
Using nested subscriptions its easy, On each http response subscription we will call another http call
like below
import { Component, VERSION, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
constructor(private httpClient: HttpClient){
}
userDetails: any;
ngOnInit(){
this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1')
.subscribe((res: any)=>{
const userId = res.userId
this.httpClient
.get('https://jsonplaceholder.typicode.com/users/'+userId)
.subscribe(userDetails=>{
console.log(userDetails)
})
})
}
}
In the above example, as you can see, we are requesting a post with post id 1 and after getting a response using subscription we are requesting for author details.
Nested subscription is not always good, always remember If you use n number of subscriptions then you should unsubscribe all the subscription otherwise there is always chances of memory leakage.
Using mergeMap or concatMap or switchMap.
mergeMap, concatMap and switchMap high-end rxjs operators have their use cases, you will notice the difference between them when you large number of Http stream
for eg. implementing search functionality, in that might we need to call Http request number of times as user enters, then might be the case you need to find the best operator among them.
to know the difference between mergeMap, concatMap or switchMap, go through this post.
for our problem of getting author details, we can use any of the operators. we will use mergeMap operator
import { Component, VERSION, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
constructor(private httpClient: HttpClient){
}
userDetails: any;
ngOnInit(){
this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1')
.pipe(mergeMap((res: any)=> this.httpClient
.get('https://jsonplaceholder.typicode.com/users/'+res.userId)))
.subscribe((authorDetails: any)=>{
console.log(authorDetails)
})
}
}
mergeMap internally subscribe to the source observable and pass response data to the inner Http observable
So, if we have 3 to 4 or n number of sequential Http calls then we don’t need to subscribe every Http calls, mergeMap will take care of it.
you can find example here on stackBlitz
ForkJoin
forkJoin is very helpful rxjs operator when we need to deal with Http calls parallelly
It’s kind of the same functionality as Promise.all, where all calls happen simultaneously. After all, calls get resolved. It emits value
for eg. If you want all posts and authors at once. then making call parallelly is a better option
import { Component, VERSION, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { mergeMap } from 'rxjs/operators';
import { forkJoin } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
constructor(private httpClient: HttpClient){
}
posts: any[];
authors: any[];
userDetails: any;
ngOnInit(){
console.log('abs')
forkJoin(
[this.httpClient
.get('https://jsonplaceholder.typicode.com/posts'),
this.httpClient.get('https://jsonplaceholder.typicode.com/users')
])
.subscribe(res=>{
console.log(res);
},err=>{
console.log('error',err)
})
}
}
Above, we have used forkJoin for getting posts and authors at once. forkJoin will emit data only when both the Http observable completed.
If any of it fail or both http observables fails then it will goto error block
please find example here on stackBlitz.
Converting HTTP observable to promise
If you convert Http observable to promise using the toPromse method, then we can use it as a normal promise.
it’s look like nested subscription, but there is no fear of memory leakage, once resolved then that’s it. No need to destroy it.
for eg.
import { Component, VERSION, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
constructor(private httpClient: HttpClient){
}
userDetails: any;
ngOnInit(){
this.httpClient.get('https://jsonplaceholder.typicode.com/posts/1')
.toPromise()
.then((res: any)=>{
this.httpClient
.get('https://jsonplaceholder.typicode.com/users/'+res.userId)
.toPromise()
.then(authorDetails=>{
console.log(authorDetails)
})
})
}
}
find example here on stackBlitz
Conclusion:
It depends upon the condition which one to use at what time. But in case n number of Http calls you need to find the best way to handle it.