134

I'm wondering what are the differences between Observable.combineLatest and Observable.forkJoin?

As far as I can see, the only difference is forkJoin expects the Observables to be completed, while combineLatest returns the latest values.

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Tuong Le
  • 18,533
  • 11
  • 50
  • 44
  • 4
    Note: In rxjs6+ these are now just `combineLatest()` and `forkJoin()` functions that create an observable. They do the same, but the syntax is different. Don't confuse `combineLatest` from `rxjs/operators` which is a 'pipeable' operator. If you import the wrong one you'll get errors. – Simon_Weaver Jan 08 '19 at 00:33

4 Answers4

200

Not only does forkJoin require all input observables to be completed, but it also returns an observable that produces a single value that is an array of the last values produced by the input observables. In other words, it waits until the last input observable completes, and then produces a single value and completes.

In contrast, combineLatest returns an Observable that produces a new value every time the input observables do, once all input observables have produced at least one value. This means it could have infinite values and may not complete. It also means that the input observables don't have to complete before producing a value.

GregL
  • 37,147
  • 8
  • 62
  • 67
  • @GregL is there a function that works like forkJoin but would work also with fail http calls? – Tukkan Sep 11 '17 at 16:22
  • 2
    @Tukkan, I would chain an operator that recovers from errors to each http observable, so that you define the value(s) to use for each request on error, rather than looking for an operator that will combine multiple observables that might error (I'm not sure such an operator exists). Operators that recover from errors include `.catch()`, `.onErrorResumeNext()`, and possibly `.retry()` (if the Http call might fail intermittently). – GregL Sep 13 '17 at 00:42
  • 1
    Just to clarify, they both produce arrays. – Simon_Weaver Jan 08 '19 at 00:37
  • @Simon_Weaver Not necessarily. In the case of `combineLatest()`, you can supply a projection function to specify how to produce an output value from the latest values from the input observables. By default, if you don't specify one, you will get an array of the latest values emitted. – GregL Jan 09 '19 at 03:13
  • Each emission from combineLatest is an array of the collected values. I just wanted to add that since you’d only mentioned array for forkjoin. So in that way they’re the same. And yes there are always subtleties with RxJS so if I missed something can you clarify what you meant. – Simon_Weaver Jan 09 '19 at 03:15
  • 3
    In RxJS7 you can pass an object to `combineLatest` and then you don't need to worry about getting the order mixed up in the pipe. eg. `combineLatest({ one: of(1), two: of(2)}).subscribe(({ one, two }) => { console.log(one + two); })` would output 3. The same functionality exists on `forkJoin` since version 6. This is MUCH nicer to work with than an array. Note: RXJS isn't yet in Angular 12 if you happen to be using that. – Simon_Weaver Jul 02 '21 at 04:39
41

forkJoin - When all observables are completed, emit the last emitted value from each.

combineLatest - When any observable emits a value, emit the latest value from each.

Usage is pretty similar, but you shouldn't forget to unsubscribe from combineLatest unlike forkJoin.

Dmitry Grinko
  • 13,806
  • 14
  • 62
  • 86
  • 1
    if one if request fails all the nested requestes gets cancelled automatically, how can I solve this? – Sunil Garg Apr 07 '19 at 08:58
  • 1
    @SunilGarg You should use forkJoin or combineLatest if you want to get all results only. If you don't care all results you should use subscriptions separately. – Dmitry Grinko Apr 07 '19 at 17:54
  • can you answer this https://stackoverflow.com/questions/55558277/how-to-send-independent-http-request-in-sequence-angular – Sunil Garg Apr 08 '19 at 07:05
  • can you explain why unsubscribe is required with code example – Sunil Garg Sep 27 '20 at 06:00
  • This doesn't mention the fact that `combineLatest` only starts emitting after all observables emit at least one value. – devmiles.com Aug 29 '23 at 09:43
28

combineLatest(...)

runs observables in parallel, emitting a value each time an observable emits a value after all observables have emitted at least one value.

combineLatest example

forkJoin(...)

runs observables in parallel, and emits a single value once all observables have completed.

forkJoin example

Consideration for error handling:

If any of the observables error out - with combineLatest it will emit up to the point the error is thrown. forkJoin will just give back an error if any of the observables error out.


Advanced note: CombineLatest doesn't just get a single value for each source and move onto the next. If you need to ensure you only get the 'next available item' for each source observable you can add .pipe(take(1)) to the source observable as you add it to the input array.

LocalPCGuy
  • 6,006
  • 2
  • 32
  • 28
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • Thanks for error handling considerations. The error handling is quite hard to understand in case of multiple observables. – D Deshmane Apr 01 '19 at 09:04
  • 2
    I believe you have misinterpreted what the `concat()` in the `combineLatest()` code is doing. It looks to me like the `Array.prototype.concat` method, not the RxJS `concat` method. Assuming I am right, then this answer is misleading and incorrect, as `combineLatest()` does not execute observables one by one, in sequence. It executes them in parallel, same as `forkJoin()`. The difference is in how many values are produced and if the source observables have to complete or not. – GregL Oct 05 '19 at 00:25
  • @GregL Array.concat is meaningless with observables. I absolutely rely on the sequential nature of concat - the key thing to understand is that you don’t get any output values until they’ve all executed. – Simon_Weaver Oct 05 '19 at 00:27
  • I was referring to this code in the sample source code you posted: `[source].concat(observables)`, assuming that is what you meant when saying that "`combineLatest` internally uses `concat`". It seems to me like you are confusing array concat with RxJS concat. The latter indeed executes the input observables in sequence, waiting until each is complete. But that is not used by `combineLatest()`, that is a separate operator entirely. – GregL Oct 05 '19 at 00:52
  • 7
    I believe `combineLatest()` and `forkJoin()` both run in parallel. Running the code below outputs about 5000 for both. `const start = new Date().getTime();` `combineLatest([of(null).pipe(delay(5000)), of(null).pipe(delay(5000)), of(null).pipe(delay(5000))]).subscribe(() => console.log(new Date().getTime() - start));` `forkJoin([of(null).pipe(delay(5000)), of(null).pipe(delay(5000)), of(null).pipe(delay(5000))]).subscribe(() => console.log(new Date().getTime() - start));` – Jeremy Oct 28 '19 at 16:20
  • 3
    so now finally, is combineLatest works in parallel?, id so then answer is misleading and need to update the answer or flag it – Sunil Garg Sep 27 '20 at 06:05
0

There is a situation in Angular which would explain it better. Assume there is a change detection in Angular component, so the latest value is changed. In the pipe and tap methods of combineLatest, the code will be triggered as well. If the latest value is changed N times by the change detection, then the tap methods is also triggered N times as well.

Bigeyes
  • 1,508
  • 2
  • 23
  • 42