This chapter follows Cancellation with AbortController.
An Observable (in the Rx sense) pushes zero or more values to a subscriber over time. It is lazy: nothing runs until something subscribes. That differs from a Promise, which starts work when created and delivers one settlement.
Observables are not part of ECMA-262; production code typically uses RxJS or framework-specific wrappers.
When observables fit
Consider observables when at least one applies:
- You need many values over time (live data, repeated events)
- Subscribers must unsubscribe to stop work or free resources
- You want operator pipelines (debounce, switchMap, merge) on incoming events
- Your framework standardizes on reactive streams
For a single HTTP response or one-shot file read, promises or async/await stay simpler. For lazy pull over async steps, async iterators may suffice—see Async iterators and for await..of.
Minimal RxJS example
Install RxJS in your project (npm install rxjs), then create a cold observable that emits on an interval:
import { interval } from "rxjs";import { take, map } from "rxjs/operators";
const ticks = interval(1000).pipe( take(3), map((n) => `tick ${n}`),);
const sub = ticks.subscribe({ next: (v) => console.log(v), complete: () => console.log("done"),});
// sub.unsubscribe() stops earlyCold observables re-run their producer per subscriber; hot observables share one producer (multicasting)—choose based on whether each subscriber needs its own side effect.
Push versus pull
| Direction | Single value | Multiple values |
|---|---|---|
| Pull | Function return | Iterator / generator |
| Push | Promise settlement | Observable emissions |
Promises are eager once created (the executor runs immediately). Observables are often lazy until subscribe. fetch plus AbortController covers many cancelable single-shot cases; observables shine when the same stream feeds many operators or subscribers.
Observable versus Promise
| Observable | Promise | |
|---|---|---|
| Values | Many over time | One future value |
| Cancel | unsubscribe (library-dependent) | Not built-in; use AbortSignal with supporting APIs |
| Start | Often on subscribe | Immediately when constructed |
| Operators | Rich stream transforms (RxJS) | then / catch chaining |
Promises are not obsolete—they are the default contract for async functions and most modern host APIs.
Relation to the rest of the series
Start the async story from the overview Asynchronous data handling if you landed here first. For thread scheduling behind timers and promise microtasks, see The event loop, tasks, and microtasks.
That completes the Asynchronous JavaScript series. Continue with A quick introduction to modules.