An Observable is a producer of a stream of values over time that you consume by subscribing. Where a promise resolves once, an observable can emit many values, then either complete or error — and it's lazy and cancellable: nothing happens until you subscribe, and unsubscribing tears the producer down. It's the model behind RxJS and reactive UIs.
Implement a minimal Observable. The constructor takes a subscribeFn(observer) run on each subscribe (it may return a teardown function). subscribe accepts an observer ({ next, error, complete } or a plain next function) and returns a subscription with .unsubscribe(). Add static of/from and the map/filter operators.
class Observable {
constructor(subscribeFn) {} // subscribeFn(observer) -> teardown?
subscribe(observer) {} // -> { unsubscribe() }
map(fn) {} // -> Observable
filter(predicate) {} // -> Observable
static of(...values) {} // emit each value, then complete
static from(iterable) {} // emit each item, then complete
}
Observable.of(1, 2, 3)
.filter((x) => x % 2 === 1)
.map((x) => x * 10)
.subscribe((v) => console.log(v)); // 10, 30
const sub = clicks.subscribe({ next: handle, complete: () => {} });
sub.unsubscribe(); // stop listening; teardown runs
next(value), and signals the end with complete() or error(err).error, complete, or unsubscribe, no further next values are delivered.subscribeFn runs per subscribe, so each subscriber gets its own independent run (subscribing is what starts the work).subscribe returns { unsubscribe }; unsubscribing runs the teardown subscribeFn returned (e.g. clearInterval).map/filter return a new Observable that subscribes to the source and transforms/forwards its values; they don't mutate the source.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.