RxJS is Reactive Extensions of JavaScript. It is a library to work with Observables which make developers life easier. RxJS provides various operators to work with in Angular. In this article we will discuss about RxJS Observable forkJoin operator, its definition, real world uses, error handling, is it deprecated etc.
What is RxJS forkJoin
forkJoin is a RxJS Observable Operator which works similar to Promise.all in JavaScript. It takes array of observables in input and emits value once all the observables are finished loading in parallel.
Is it deprecated ?
The big question about RxJS forkJoin
operator is- is it deprecated ? The answer is- Kind of NO
I will explain what I mean!
Initially forkJoin
was accepting object arguments as rest parameter. That is deprecated and no longer used.
Now they updated the operator and ask to provide array of observables instead of object arguments. As the line itself saying in above screenshot- @deprecated — Use the version that takes an array of Observables instead
How RxJS forkJoin operator works
ForkJoin
subscribes all the observables passed to that and runs them in parallel. It collects the last emitted values from all of the passed observables once they are done and gets you result in array.
import { Component, OnInit } from '@angular/core';
import { forkJoin, Observable, of } from 'rxjs';
@Component({
selector: 'app-forkjoin',
templateUrl: './forkjoin.component.html',
styleUrls: ['./forkjoin.component.scss']
})
export class ForkjoinComponent implements OnInit {
observables$: Observable = of([1, 2, 3, 4, 5]);
string$: Observable = of(['Test', 'String']);
constructor() { }
ngOnInit(): void {
forkJoin([this.observables$, this.string$]).subscribe(data => {
console.log(data);
});
}
}
Above code will produce this output:
Real World Uses
The big question which will come in your mind is- I know what you explained. Just let me know the Real World Examples of forkJoin
operator..
So here are some great examples where RxJS forkJoin
is best to use:
- Call Multiple Remote APIs in Parallel– for ex you want to get result of User along with its Address, Social Stats, Transaction History etc
- Call Mixed Observables– for ex you want to get result of an API, a transformed object data
Let’s understand each of them with example.
Call multiple APIs in parallel
forkJoin
comes as handy operator when situation like this happens. I have an example where I have to call 3 APIs simultaneously. I am using https://fakestoreapi.com to simulate the example.
forjoin.service.ts
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
@Injectable()
export class ForkJoinService {
constructor(private httpClient: HttpClient) {
}
public getUserById(userId: number) {
return this.httpClient.get('https://fakestoreapi.com/users/' + userId);
}
public getCartById(cartId: number) {
return this.httpClient.get('https://fakestoreapi.com/carts/' + cartId);
}
public getProductById(productId: number) {
return this.httpClient.get('https://fakestoreapi.com/products/' + productId);
}
}
Call the above methods from component.
import { Component, OnInit } from '@angular/core';
import { forkJoin } from 'rxjs';
import { ForkJoinService } from './forkjoin.service';
@Component({
selector: 'app-forkjoin',
templateUrl: './forkjoin.component.html',
styleUrls: ['./forkjoin.component.scss']
})
export class ForkjoinComponent implements OnInit {
constructor(private forkJoinService: ForkJoinService) { }
ngOnInit(): void {
// simulate 3 requests
forkJoin({
userDetail: this.forkJoinService.getUserById(1), // getting user for id 1
productDetail: this.forkJoinService.getProductById(1), // getting product by id 1
cartDetail: this.forkJoinService.getCartById(1) // getting cart by id 1
})
.subscribe(({ userDetail, productDetail, cartDetail }) => {
console.log('userDetail', userDetail);
console.log('productDetail', productDetail);
console.log('cartDetail', cartDetail);
});
}
}
The above code will produce result like this.
If you go to Network tab, you will notice that each APIs are called in parallel.
Call Mixed Observables
Another case to use forkJoin
can be- you have to call mixed type of observables together. Check the below example code.
import { Component, OnInit } from '@angular/core';
import { forkJoin, Observable, of } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { ForkJoinService } from './forkjoin.service';
@Component({
selector: 'app-forkjoin',
templateUrl: './forkjoin.component.html',
styleUrls: ['./forkjoin.component.scss']
})
export class ForkjoinComponent implements OnInit {
observables$: Observable = of([1, 2, 3, 4, 5]);
constructor(private forkJoinService: ForkJoinService) { }
ngOnInit(): void {
const modifiedItems = this.observables$.pipe(map(items => {
return items.map(x => x * 10);
}));
forkJoin({
userDetail: this.forkJoinService.getUserById(1),
modifiedItems
})
.subscribe(({ userDetail, modifiedItems }) => {
console.log('userDetail', userDetail);
console.log('modifiedItems', modifiedItems);
});
}
}
For this example I have taken an API observable and an observable of integer numbers. I want both results at same time with multiple of 10
in each integers.
Here is the output.
Handling Errors in RxJS forkJoin Operator
If any of the observable throws error then you will loose the result of any other observable that would or have already completed. To handle this thing, you need to apply catchError
operator to each observables.
Output without catchError
Output with catchError
As you already saw in above screenshot, if you have handled errors with catchError
operator, you will get the result even if one observables fails.
Use catchError
operator like this.
import { Component, OnInit } from '@angular/core';
import { forkJoin, Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { ForkJoinService } from './forkjoin.service';
@Component({
selector: 'app-forkjoin',
templateUrl: './forkjoin.component.html',
styleUrls: ['./forkjoin.component.scss']
})
export class ForkjoinComponent implements OnInit {
observables$: Observable = of([1, 2, 3, 4, 5]);
constructor(private forkJoinService: ForkJoinService) { }
ngOnInit(): void {
const modifiedItems = this.observables$.pipe(map(items => {
return items.map(x => x * 10);
}));
forkJoin({
userDetail: this.forkJoinService.getUserById(1).pipe(catchError(err => of(err))),
modifiedItems: modifiedItems.pipe(catchError(err => of(err)))
})
.subscribe(({ userDetail, modifiedItems }) => {
console.log('userDetail', userDetail);
console.log('modifiedItems', modifiedItems);
});
}
}
Conclusion
This article covered definition, real world use cases, error handling of forkJoin
operator. If you liked the article, share with others. What’s next? I would recommend you check this article: Real World Examples of 5 Common Observable Operators
Also Read: 5 Best Ways To Optimize Angular For Scaling (2021)