Photo by PJH on Unsplash

On this page

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 early

Cold 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.

Search articles

Type a word to search published articles.

Open the search page