All topics
RxJSintermediate

combineLatest and forkJoin

Explain how combineLatest and forkJoin each combine multiple Observables, and when to use one over the other.

Both combineLatest and forkJoin take multiple source Observables and combine their values into a single emission, but they answer fundamentally different questions and interviewers like this pair specifically because picking the wrong one produces very different (and sometimes very confusing) timing behavior.

combineLatest is like a live scoreboard updating the moment any single game's score changes, always showing the latest known score for every other game too. forkJoin is like waiting for every runner in a relay race to finish before announcing the combined final results all at once — and if one runner never crosses the finish line, the announcement never happens.

Key Concepts

1
combineLatest([a$, b$, c$]) waits for every source Observable to emit at least once, and after that, it re-emits a new combined array (or object, with the object-argument form) every single time *any one* of the sources emits again, always pairing the new value with the most recent value from every other source. This is the right tool for continuously reactive combinations — like a filtered product list that should update whenever either the search term or the sort order changes, treating both as ongoing, live inputs.
combineLatest([a$, b$, c$])
2
forkJoin({ users, posts }) behaves much more like Promise.all() — it waits for every source Observable to complete, and only then emits a single combined result containing each source's *final* emitted value, immediately completing itself afterward. This is the right tool for a one-time "fetch several independent things in parallel, then proceed once all are done" scenario, like loading a dashboard's several independent data sources before rendering.
forkJoin({ users, posts })Promise.all()
3
A key interview trap is that forkJoin requires every input Observable to actually complete — an Observable that never completes (like a BehaviorSubject or an interval) will cause forkJoin to simply hang forever, never emitting anything, since it's waiting for a completion signal from every source that one of them will never send. A correct answer flags this explicitly, since it's a genuinely common real-world bug: accidentally passing a live, never-completing stream into forkJoin expecting it to behave like a normal one-time HTTP-style Observable.
forkJoinBehaviorSubject