forkJoin vs combineLatest

Learning RxJS is quite challenging and it is undoubtedly the great tool for front-end developers. We cannot grasp entire RxJS operators in one go, but we may encounter some operators for our use cases. One such operators are forkJoin and combineLatest.

forkJoin is a special operator from RxJx that let’s you combine / accept multiple ObservableInputs and returns the objects of values with exact shape of input.

In a situation, imagine you have a scenario to fetch multiple API’s response and load the data based on all responses.

We naturally go for calling all the api’s one by one, like seen below.

  
  ngOnInit() {
    this.data.getVehicles().subscribe(data => console.log(data));
    this.data.getUsers().subscribe(data => console.log(data));
    this.data.getHackers().subscribe(data => console.log(data));
    this.data.getArticles().subscribe(data => console.log(data));
  }

What if certain areas of UI require all the API’s response together?

forkJoin comes to play if such a scenario arises. forkJoin executes all Observables parallely and completes the execution only if all Observables emit the result.

Even in an unlikely scenario where if one of the API is delayed, the forkJoin waits for it to complete.

Our code becomes like this.

runForkJoin() {
    const observable = forkJoin({
      vehicles: this.data.getVehicles(),
      users: this.data.getUsers(),
      hackers: this.data.getHackers(),
      articles: this.data.getArticles(),
    });

    observable.subscribe({
      next: (value) => console.log(value),
      complete: () => console.log('Completes with Success!'),
    });
}

// console.log output
// articles: Array[26]
// hackers: Array[30]
// users: Array[10]
// vehicles: Array[25]
// Completes with Success!
//

The final response object will have properties that are defined in forkJoin such as vehicles, users, hackers and articles.

If any one HTTP call fails, the entire forkJoin mechanism fails and throws an error, eventually we can catch them in the error block.

{
    "headers": {
        "normalizedNames": {},
        "lazyUpdate": null
    },
    "status": 404,
    "statusText": "Not Found",
    "url": "https://6231f15359070d92733f63aa.mockapi.io/api/v1/articles1",
    "ok": false,
    "name": "HttpErrorResponse",
    "message": "Http failure response for https://6231f15359070d92733f63aa.mockapi.io/api/v1/articles1: 404 Not Found",
    "error": "Not found"
}

combineLatest is similar to forkJoin, except that it combines the latest results of all the observables and emits the combined final value. So until each observable is completed, the subscription block emits the result.

  
  runCombineLatest() {
    const observable = combineLatest({
      vechicles: this.data.getVehicles(),
      users: this.data.getUsers().pipe(delay(2000), startWith(null)), // Simulate network delay
      hackers: this.data.getHackers(),
      articles: this.data.getArticles().pipe(delay(500), startWith(null)),  // Simulate network delay
    });

    observable.subscribe({
      next: (value) => console.log(value),
      complete: () => console.log('Completes with Success!'),
    });
  }

// After runCombineLatest
// {vechicles: Array[25], users: null, hackers: Array[30], articles: null}
// 
// After 500 milliseconds
// {vechicles: Array[25], users: null, hackers: Array[30], articles: Array[26]…}
// 
// After 2000 milliseconds
// {vechicles: Array[25], users: Array[10], hackers: Array[30], articles: Array[26]}
// 

For the sake of network delay, we introduced delay and startsWith. So initially the users and articles property starts with null and as soon as the data is retrieved. Both the properties are populated with latest responses.

And all responses are combined.

In conclusion, both forkJoin and combineLatest produce similar results but forkJoin emits the response only after all the Observables completes. Whereas combineLatest keeps emitting responses as soon as anyone Observable starts completing.

Source code can be found in StackBlitz