We’ve been using RXJS often, and noticed that we had a vague mental model for this reactive / stream-based programming. At the beginning, we thought of streams as “weird arrays”, which is not a very helpful mental model.
The RXJS team has provided a very precise definition of the components of the library, but it doesn’t deal with the mental model. As we say on our team, “this document is a list of ingredients, but they haven’t said what they’re making with them”. (The “omelette vs eggs” communication trap.) We want to know how we should conceive of this thing, not just what it’s made of.
After lots of practice and reading, here’s how we think about RXJS:
- RXJS is a library for working with streams of events.
-
pipe
transforms a stream of events- there are many different transform methods that can be used inside a
pipe
- there are many different transform methods that can be used inside a
-
subscribe
lets us react to each event as it arrives
Mental Model: Household Plumbing
An observable (stream) is like the plumbing in a house.
An event source is like the city water supply that arrives at the house.
Calling .pipe(...)
is like hiring a plumber to install a new faucet or appliance.
There might be filters or transformations between the PVC/copper pipe to change or
restrict what can come out. (For example, you might have an on-demand hot water heater that
changes the temperature of the water that passes through it.)
Calling .subscribe()
is like turning on the faucet. You’re ready for stuff to come out,
and you probably want to use it for something. You can’t control what comes out; you get
what you get. If it’s bad, blame the plumber or the city. You’re just the consumer.
const cityWaterSupply = new Subject<any>();
const kitchenTap = cityWaterSupply.pipe(
filter(water => doesNotContainPathogens(water)),
map(water => applyHeat(water)),
filter(water => doesNotExceed110DegreesFarenheitAccordingToCsaSpecifications(water))
);
kitchenTap.subscribe(water => washHandsWith(water));
cityWaterSupply.next("bad water 🦠");
cityWaterSupply.next("nice water");