RxJS forkJoin: Definition and Real World Uses

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!

forkJoin is deprecated if you pass object arguments
forkJoin is deprecated if you pass object arguments

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:

forkJoin output
forkJoin 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.

forkJoin Multiple API Call Result
forkJoin Multiple API Call Result

If you go to Network tab, you will notice that each APIs are called in parallel.

forkJoin Network Response
forkJoin Network Response

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.

forkJoin Output with Mixed Observables
forkJoin Output with Mixed Observables

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

There is not any result if one observable fails as shown in above screenshot

Output with catchError

You will get result of ones which are completed

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)

7 Best Ways To Improve Angular Code (2021)

Leave a Reply

Your email address will not be published. Required fields are marked *