Understanding forkJoin
forkJoin accepts multiple input observables and patiently waits until every one of them has completed. Only then does it emit a single combined output, composed of the final value produced by each observable.
The resulting observable emits exactly once, and that emission happens only after all inner streams have finished. If any inner stream never completes, the combined stream will remain open indefinitely. Likewise, if any inner stream emits an error, that error is propagated immediately and the entire result fails.
Here is the step-by-step behavior of the operator:
- It subscribes to all of the provided observables concurrently.
- Whenever one of those observables emits a value, that value replaces the cached value for that particular observable.
- When an observable completes, the operator verifies whether all of the others have also completed.
- Once every observable has completed, the operator emits a single array or object containing the last value from each source.
- After the emission, the observer receives a complete notification.
- If any source observable signals an error, that error notification is forwarded to the observer directly.
In the visualization below, the forkJoin operator merges two streams, labeled A and B. Only when both have produced their final values does the combined stream deliver a single aggregated output:

The following code sample reproduces the exact scenario illustrated above:
const a = stream('a', 200, 3);
const b = stream('b', 500, 3);
forkJoin(a, b).subscribe(fullObserver(operator));
You can also experiment with this behavior in the interactive demo below:
When to Use forkJoin
This operator is ideal when you have multiple streams but you are only interested in their final emitted values. Typically, each of those streams will produce just a single value. A common use case is firing several independent network requests and acting only once all responses have arrived. The behavior closely mirrors Promise.all. However, if any source emits more than one value, only the very last one is retained and used in the final result.

