Observables started making sense to me the day I tried to build a live search + notification system in Angular. Promises were fine for one‑time API calls, but as soon as I needed “keep listening, keep updating, and let me cancel when I’m done,” they started to feel like the wrong tool. Observables, on the other hand, behaved exactly like that data firehose I wanted—push data to me whenever something changes, and I’ll react.
Why Observables clicked for me
The first real project where Observables helped me was a dashboard that showed:
- Live user notifications
- A search box that updated results as you typed
- A “last seen online” status that updated in real time
- API calls that were hard to cancel
- Event listeners I kept forgetting to remove
- Multiple nested callbacks that felt like “Promise soup”
When I switched those workflows to Observables, three things instantly improved:
That’s why I like to think of Observables as live data pipelines rather than one‑time operations.
- I stopped worrying about “how often” things happened; I just subscribed to streams.
- I could easily combine multiple data sources (API + timer + events).
- I could unsubscribe in one place and cleanly shut everything down.
Observables in human terms: the subscription story
The newspaper analogy is actually very close to how Observables feel in real apps:
- You subscribe once.
- The publisher (Observable) keeps sending you new editions (values).
- You can stop the subscription whenever you want (unsubscribe).
- If the newspaper shuts down, you get no more editions (complete).
A small observable from scratch (without any library jargon)
Let’s imagine we want a tiny “ticker” that emits a message every second, then stops after 5 times.
Conceptually, here’s what we want:
- We start the ticker.
- Every second, it says “tick 1”, “tick 2”, etc.
- After 5 ticks, it says “done” and stops.
- We can also cancel it early if we get bored.
function createTicker(intervalMs = 1000, maxTicks = 5) {
return {
subscribe(observer) {
let count = 0;
const id = setInterval(() => {
count += 1;
observer.next(`Tick #${count}`);
if (count >= maxTicks) {
clearInterval(id);
observer.complete();
}
}, intervalMs);
// Return unsubscribe logic
return {
unsubscribe() {
clearInterval(id);
observer.complete && observer.complete();
},
};
},
};
}
// Usage
const ticker = createTicker(1000, 5);
const subscription = ticker.subscribe({
next: (value) => console.log(value),
complete: () => console.log("Ticker finished"),
});
// If we want to stop early:
// setTimeout(() => subscription.unsubscribe(), 2500);
This is the core idea Observables build on:
Libraries like RxJS give you a ton of operators around this concept, but the heart stays the same.
- There is a producer (createTicker)
- There is a consumer (subscribe)
- The consumer gets values over time via next
- The consumer can unsubscribe to stop everything
How Observables helped in a real API + UI scenario
Scenario: live search with cancel + retry
I built a search page where:
Doing all that with Promises required manual tracking of AbortControllers and retry logic. With Observables (like RxJS), it felt more natural:
- Users type in a search box.
- The app calls the API and shows results.
- If the user types again before the previous request finishes, the old one should be cancelled.
- If the network fails briefly, the search should retry automatically a couple of times.
- Convert input events into an Observable stream.
- Debounce the typing to avoid spam.
- Switch to a new API call when the user types again (cancelling the old one automatically).
- Add retry logic in the pipeline.
I no longer thought in terms of “call this, then this, then this” but instead “take this stream of user input and transform it into a stream of results.”
Observables vs Promises in practice (not just in theory)
From real use, here’s how they differ in my head:
Promises
Observables
I often start with a Promise mentality: “I need to fetch some data.”
If I later realize that I need live updates, cancellation, or multiple emissions, I switch to Observables.
- Great for: one‑time operations (login, initial fetch, file upload).
- You call them → they do something → they resolve or reject once.
- Harder to cancel and awkward to repeat or combine for continuous events.
- Great for: repeated or continuous data flows (events, WebSockets, intervals).
- You subscribe once → you keep getting values until you unsubscribe.
- Easy to compose, transform, merge, and cancel.
If I later realize that I need live updates, cancellation, or multiple emissions, I switch to Observables.
Real‑world use cases where Observables shine
Here are scenarios from my own projects where Observables felt like the best tool:
1. WebSocket live updates
For a real‑time notification panel:
2. Scroll + resize + user activity tracking
- The WebSocket connection stayed open.
- Every time the server pushed data, the Observable emitted a new value.
- When the user navigated away, I just unsubscribed, which closed the socket.
I once had to detect whether the user was “active” (scrolling, clicking, typing) and send a heartbeat to the server only when they were actually doing something.
- I turned events like scroll, click, keydown into Observables.
- Merged them into a single “userActivity$” stream.
- Used operators like debounce and distinctUntilChanged to limit noise.
- Subscribed to that merged stream to trigger heartbeats.
With callbacks and Promises, this would have been a tangled mess of listeners and timers.
3. Chained API calls based on previous results
Example: fetch profile → then fetch related recommendations → then open a WebSocket for personalized offers.
- With Promises, you end up chaining .then() or async/await with try/catch plus manual cancellation.
- With Observables, you can express “after this, switch to that, then listen continuously” in one pipeline and cancel the whole chain via unsubscribe.
When I don’t use Observables
To keep things simple, I avoid Observables when:
- I just need a single one‑off API call on page load.
- The logic is straightforward and doesn’t require streams.
- The extra abstraction might confuse a beginner on the team.
Choosing Observables wisely
Here’s the rule of thumb I use now:
Use Promises when:
Use Observables when:
When building Angular apps, Observables feel almost natural because HttpClient, forms, routing, and many libraries already speak in streams. In plain React or vanilla JS, I reach for them when things grow beyond simple fetch calls and basic event listeners.
- You’re doing something once.
- You don’t need cancellation.
- It’s a simple request/response pattern.
- You’re dealing with events, streams, or anything repeated.
- You need to combine multiple sources (user input + API + timers).
- You want easy cancellation and advanced operators.
Conclusion: why Observables are worth learning
Observables can look intimidating at first—especially once operators and marble diagrams show up—but at their core, they’re just data over time with a clean subscribe/unsubscribe mechanism.
They helped me:
If you’ve ever felt that your async code is turning into spaghetti, it’s a strong sign you’re ready to think in streams. Promises are like single emails; Observables are like ongoing chat conversations. Once you see your app as a set of conversations, Observables start to feel very natural.
- Avoid memory leaks from forgotten event listeners
- Handle live updates without rewriting half my code
- Cancel work that was no longer needed
- Combine user interactions, timers, and API calls into a single, readable flow
