Stale-while-revalidate — cache first, network after
Pattern
We have a previous response in the cache. We show it instantly so the screen isn't empty, and in parallel we go to the network for fresh data. When the network answers, we update the UI.
The problem it solves
The user shouldn't stare at an empty screen if we already have "good enough" data. Imperative code mixes synchronous cache reads, an async request, and a cache write into one hard-to-test mess.
Operators and why they matter
concat — guarantees the order: cache first, then network. No races.
of(cache) — hands over the cached value as an Observable so it can be chained with the network one.
defer — defers the cache read/write until subscribe time.
tap — writes the fresh response into the cache as a side effect.
map — tags the source ('cache:...' or 'network:...') for the UI.
Gotchas
merge instead of concat — the network can answer before the cache emits, and the user sees a "flicker" of fresh → stale → fresh.
Write to the cache ONLY after a successful response. Otherwise the stale value becomes wrong after the next reload.
For long-lived sources (interval, WebSocket), concat without take hangs — it waits for the first source to complete.
What you get
The UI shows stale data instantly, then updates smoothly with fresh data. The data contract is the same — the UI can't tell the source apart.