Observables are at Stage 1 in TC39's standardization process, meaning they might become part of JavaScript eventually. RxJS implements them now, and Angular 2 adopted them for HTTP and events. The question is whether Observables are genuinely better than Promises or just more complex.
After using RxJS with Angular 2, I think the answer is: both.
What Observables Are
Promises represent single async values. Observables represent streams of values over time:
// Promise: One value
fetch('/api/user')
.then(response => response.json())
.then(user => console.log(user));
// Observable: Multiple values
clicks$ // Observable of click events
.map(event => event.clientX)
.filter(x => x > 100)
.subscribe(x => console.log(x));
Observables handle:
- Multiple values (Promises handle one)
- Cancellation (Promises can't be cancelled)
- Synchronous or asynchronous (Promises are always async)
- Lazy execution (Promises execute immediately)
This flexibility makes Observables more powerful—and more complex.
The RxJS Implementation
RxJS is JavaScript implementation of ReactiveX—reactive programming pattern with implementations in many languages.
import { Observable } from 'rxjs';
const numbers$ = Observable.of(1, 2, 3, 4, 5);
numbers$
.map(n => n * 2)
.filter(n => n > 5)
.subscribe(
value => console.log(value),
error => console.error(error),
() => console.log('complete')
);
RxJS provides 100+ operators for transforming, combining, and manipulating streams. The power is immense—and overwhelming.
Where Observables Excel
Observables shine for specific use cases:
Event streams:
const clicks$ = Observable.fromEvent(button, 'click');
const doubleClicks$ = clicks$
.buffer(clicks$.debounce(250))
.map(clicks => clicks.length)
.filter(length => length === 2);
WebSocket data:
const socket$ = Observable.webSocket('ws://localhost:8080');
socket$
.retryWhen(errors => errors.delay(1000))
.subscribe(message => console.log(message));
Coordinating multiple async sources:
Observable.forkJoin(
getUserData(),
getUserPosts(),
getUserComments()
).subscribe(([user, posts, comments]) => {
// All three completed
});
For these patterns, Observables are clearer than Promise chains or callback orchestration.
The Learning Curve Problem
RxJS's power comes with cost: cognitive load.
100+ operators to learn: map, filter, reduce, scan, merge, concat, combineLatest, withLatestFrom, switchMap, mergeMap, exhaustMap, debounce, throttle, buffer, window…
Marble diagrams: Understanding operators requires reading marble diagrams—visual representations of stream transformations.
Memory leaks: Forgetting to unsubscribe creates leaks. Managing subscriptions is burden.
Debugging: Async streams are hard to debug. Understanding where/when values flow is challenging.
Hot vs cold observables: Understanding observable types and their execution behavior is non-intuitive.
This isn't trivial learning. Teams need weeks/months to become productive with RxJS.
When Promises Are Better
For many async operations, Promises are simpler:
// Promise: Clear
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// Observable: Overkill
function getUser(id) {
return Observable.fromPromise(fetch(`/api/users/${id}`))
.switchMap(response => Observable.fromPromise(response.json()));
}
Promises are better for:
- Single async values
- Simple async flows
- Teams without reactive programming experience
- Code that doesn't need cancellation
Observables are better for:
- Event streams
- Multiple values over time
- Complex async orchestration
- Cancellation requirements
Angular 2's Observable Commitment
Angular 2 uses Observables for HTTP and forms:
http.get('/api/users')
.map(response => response.json())
.subscribe(users => this.users = users);
This was controversial. Developers learning Angular must learn RxJS. The learning curve is steeper.
But for Angular's needs—complex async patterns, cancellation, composability—Observables make sense.
Whether forcing this on all Angular developers was right decision is debatable.
The TC39 Proposal
Observables being proposed for JavaScript (Stage 1) would standardize the pattern. The proposal is minimal—just Observable core, not RxJS's 100+ operators.
// Proposed Observable
const observable = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.complete();
return () => {
// cleanup
};
});
observable.subscribe(
value => console.log(value),
error => console.error(error),
() => console.log('complete')
);
This gives language-level support without committing to RxJS's full API.
Whether this reaches Stage 4 (standardization) is uncertain. Observables are powerful but add language complexity.
The Cancellation Value
One significant Observable advantage: cancellation.
const subscription = longRunningOperation$.subscribe(value => {
console.log(value);
});
// Later, cancel it
subscription.unsubscribe();
Promises can't be cancelled once started. For long-running operations, this is limitation.
fetch() with AbortController (coming) addresses this for HTTP, but Observables have cancellation built-in.
Memory Management Burden
Observable subscriptions must be cleaned up:
class Component {
subscription: Subscription;
ngOnInit() {
this.subscription = data$.subscribe(/* ... */);
}
ngOnDestroy() {
this.subscription.unsubscribe(); // Forget this = leak
}
}
This manual management is error-prone. Promises don't have this problem—they're garbage collected automatically.
Patterns exist (takeUntil, async pipe in Angular) to simplify this, but it's additional complexity.
When Observables Make Sense
Observables are valuable for:
- Applications with heavy event-driven patterns
- Teams that can invest in learning RxJS
- Projects requiring cancellation frequently
- Complex async coordination needs
Observables are overkill for:
- Simple request-response patterns
- Teams without reactive programming experience
- Projects where Promises suffice
- Codebases prioritizing simplicity
Looking Forward
Whether Observables become JavaScript standard is uncertain. RxJS provides them today for teams that need them.
The pattern is powerful but not universally better than Promises. It's different tool for different problems.
My take: learn Observables if working with Angular 2 or complex async patterns. Otherwise, Promises and async/await cover most needs with less complexity.
Reactive programming is valuable skill. Whether it should be JavaScript default is questionable.
Resources:
- RxJS
- Observable Proposal
- Learn RxJS
- RxMarbles – Interactive marble diagrams