A junior developer once asked me:
“RxJS has been around for years, and so many developers use it.
Why hasn’t JavaScript just added something like it natively?”
To which I answered:
Reactivity is mainly a frontend problem. It’s about dealing with one user’s continuously changing state — events, inputs, UI updates. The backend, in contrast, deals in stateless operations. Each request stands alone. The state lives in databases, not in-memory streams. You don’t “observe” backend data changes directly — you react through messages, queues, or databases. Kafka or Redis Streams handle that domain, not RxJS.
That was my quick answer.
But the question was too interesting to leave there — so I dug deeper.
RxJS Has Proven Itself
RxJS has powered Angular’s reactivity model for over a decade.
It’s elegant in handling asynchronous data, widely respected, and deeply integrated in the frontend ecosystem.
Yet despite its longevity and popularity, Observables never became part of JavaScript itself.
The reason is simpler — and deeper — than most realize:
RxJS solved a problem JavaScript itself never really had.
What RxJS Actually Solves
RxJS is built on the concept of Observables — objects that represent streams of asynchronous data that can emit multiple values over time.
- A Promise resolves once.
- An Observable keeps emitting.
That makes it perfect for situations like:
- Listening to continuous user input
- Handling WebSocket data
- Managing UI state that updates over time
It’s powerful and expressive — you can map, filter, debounce, combine, and merge asynchronous streams just like you manipulate arrays.
So, if it’s that powerful... why didn’t JavaScript itself embrace it?
JavaScript’s Evolution Is Conservative by Design
JavaScript’s core design philosophy is universality and minimalism.
The TC39 committee — the group that decides what gets added to the language — only standardizes features that:
- Apply across all JavaScript environments (browser, server, embedded)
- Don’t introduce unnecessary conceptual weight or complexity
RxJS breaks both of those principles.
Reactivity is mainly a frontend concern — about one user’s continuously changing state.
The backend’s problems are different: stateless operations, distributed events, and persistence handled by external systems.
The Observable Proposal That Never Landed
This wasn’t just theoretical — JavaScript almost adopted Observables.
In 2017, a TC39 proposal sought to introduce a minimal Observable type. It reached Stage 1, attracted attention… then quietly stalled.
Why It Failed
- RxJS already provided a complete, mature ecosystem.
- Observables are inherently complex — cold vs. hot, cancellation, multicasting, operator design, interop with Promises, etc.
- Frameworks had already diverged:
- Angular leaned on RxJS
- React favored hooks and state
- Vue built its own reactivity model
The committee couldn’t justify making one paradigm official when the community was already solving the same problem in multiple, incompatible ways.
So JavaScript took the conservative route:
standardize the smallest useful abstraction — Promises — and let the ecosystem handle the rest.
Promises Were the Problem JavaScript Did Have
Promises fixed a universal issue: callback hell.
Before Promises, async code was deeply nested, hard to reason about, and full of error-handling traps. Every JS developer — frontend or backend — needed a cleaner model for single-result async work. Then async/await came, making async code look synchronous — elegant and readable. That was the problem JavaScript actually needed to solve: a single-value, eventual result.
Observables, on the other hand, were about ongoing reactivity — a smaller, domain-specific need mostly confined to the frontend world.
So Promises became part of the language; Observables remained a library concern.
Signals: The Simpler Next Step in Reactivity
But reactivity didn’t disappear — it evolved.
Signals are the next iteration: smaller, simpler, and more predictable.
Signals aren’t streams of events like Observables. They’re reactive state containers that automatically track dependencies and update when values change. You don’t subscribe to them; you just declare relationships.
Example
const count = signal(0);
const doubled = computed(() => count() * 2);
count.set(5);
console.log(doubled()); // 10 — automatically updated
No subscription. No operators. No teardown.
Just reactive state, tracked automatically.
Signals shift reactivity from streams of events to graphs of state — simpler, more deterministic, and easier to optimize.
Thanks for Reading ❤️
If this article helped you, share it and give it like.
It’ll hardly take a minute, but it really motivates me to write more deep-dive articles like this.
I don’t just write “how-tos” — I write about why things are designed the way they are,so you can master them inside-out.
Further Reading
- TC39 Proposal — Observable
- TC39 Proposal — Signals
- RxJS (Reactive Extensions for JavaScript)
Top comments (0)