The dashboard needs loading from four independent endpoints: user, profile, permissions, widgets. They don't depend on each other — they can load in parallel. Show the screen only when ALL four responses arrive.
The problem it solves
Subscribe to each of the four separately and the component renders in intermediate states: "user is here, profile is still null, permissions undefined." Promise.all works, but you lose RxJS's benefits — cancellation on destroy, a single error boundary, easy composition.
Operators and why they matter
forkJoin — waits for complete from each source and emits ONCE — an array of the last values. Perfect for one-shot HTTP.
map — assembles one dashboard view model from the four DTOs.
catchError — if any request fails, return a fallback VM or an error state.
Gotchas
forkJoin NEVER emits if even one source doesn't complete. Don't feed it interval, WebSocket, or valueChanges without take(1).
combineLatest instead of forkJoin — you'll get intermediate VMs as responses arrive, with half the fields filled.
catchError outside forkJoin — a single fallback for the whole load. If you want a per-request fallback (one service down, the rest up), put catchError inside each request.
What you get
The component gets either a ready, consistent dashboard VM or a controlled error. No intermediate "half-loaded" states.