RxJS is short for reactive extensions library as implemented in JavaScript. It's built into Angular, and available on its own as well. What it really is is an implementation of a pattern called reactive programming. And as a result, there are extensions libraries in multiple different languages. RxJS is very much a power tool, best in situations where you know how your operation starts, and know how it ends, and want to make changes in the middle.
The examples below are in JavaScript. But the general concept should apply across the board. The main concepts I'm going to review are observables, subscriptions, observers, the pipe and operators. Let's start with the first.
Observables
An observable has many names, it's also referred to as a stream, or even an observable stream. But in the end, the observable pattern is a way to handle async events as collections. As a quick reminder, in programming, we often want to receive data in async mode. Making a call to retrieve information and doing other things until we have a result. Observables are one way to handle those calls and results.
For the rest of this post I'm going to use an analogy that helped me understand the observable pattern. Suppose an observable is a river. It has the potential to carry boats (events) but right now it can't, because there is a drawbridge at the start of it.
An observable is unique because it has two modes: on and off. Observables are off by default. So the stream has the ability to turn on and carry boats, but right now it's off.
const obs = of(1,2,3,4)
Example of the of operator which creates an observable from a set of items.
Subscription
Observables are turned on when we subscribe to them. This is equivalent to raising the drawbridge, allowing boats to travel down our river. We can also unsubscribe, lowering our drawbridge and turning off the flow of boats on the river.
const obs = of(1,2,3,4)
obs.subscribe(observer)
Note that observables can have more than one subscription. But it only takes one to turn "on" our observable.
Observer
Suppose we need a person to raise the drawbridge. So we pass the person to our subscription. This person is our observer. This observer will watch the river and report on every boat that flows down it.
In reality, an observer is an object. Inside the observer there are three possible methods, though only one is required. They're next, error and complete. Next tells the observable what to do with the value emitted. Error handles error cases, and complete is called when there are no more values to emit.
This is where we start to understand the value of having multiple subscribers. They can each do different things with the emitted values from the observable.
For example, suppose we have red and yellow boats. We can subscribe to the observable and the next method in our observer can filter out only the red boats, the person only relays information when it sees a red boat. And we can have another subscription with a different observer that handles only the yellow boats.
const obs = of(1,2,3,4)
const observer = {
next: (x) => console.log(x),
error: (err) => console.log(err),
complete: () => void
}
obs.subscribe(observer)
Operators and the Pipe
Now, here is where things get really interesting. Operators are the true power in observable land. Think of it like this, we can change the way the river flows, add additional landscape features and other details to alter our observable source. Note that we're not altering the boats directly, but rather the river. We're adding an ocean so that all larger boats go there and only small boats continue down the river. We're adding a blue paint waterfall so all boats that travel through it change color to blue. These changes occur on the river itself (the observable source), and when the boats flow down, they're affected.
And we can chain our operators. To do that we use a pipe. And each operator takes in an observable and returns a new one. So the final result is an observable, no matter how many operators we use. We subscribe to that observable and as a result apply to the entire chain of observables before it. So our observer is only relaying information about boats after they've passed through all our operators.
const obs = of(1,2,3,4).pipe(
onlySmallBoats(),
paintBoatsBlue()
)
const observer = {
next: (x) => console.log(x)
}
obs.subscribe(observer)
In reality, these would be real operators like map, combineLatest, race, etc.
Put it All Together
If we put all these pieces together, what is our analogy really explaining?
- Our observable is a river with a drawbridge at the head that is currently down
- The subscription is raising that drawbridge
- The subscription is passed a person, our observer
- We only need one person to raise the drawbridge (one subscription) but can have many
- The person relays information about the boats it sees flow down the river
- We can have multiple observers watching our river and relaying information about different subsets of boats
- The river can have physical features that act as operators, like blue paint waterfalls that turn boats that pass through it blue
- You can have multiple operators on the river, one after the next, piping them so boats that flow through one (output) will flow through the next(input)
- The observer will be located lower down on the river, after the final operator, and will only relay information about the boats at that time
- An unsubscribe is us lowering our drawbridge, an observer going home
- With multiple subscriptions, the observable will be "on" until no subscriptions are left, aka the last observer has gone home for the day
- An observer can go home(complete) when there are no more boats to see
- If an observer sees a dragon instead of a boat(an error) they may determine they're not feeling well and go home, observing no more boats
And that's it. No analogy is perfect, but there is a lot I like about this one.
Conclusion
A big part of learning RxJS is learning the specific operators. However, when people do that they often copy and paste the code without really understanding what's happening. RxJS is a powerful pattern and operators are really just a facade behind which we hide a bunch of gnarly callbacks.
Remember that observables aren't necessary for everything. But they make previously hard problems a lot easier. Try using fromEvent
to look at changes to a form field, it's pretty cool stuff!
Top comments (11)
The way I make sense of it may not be entirely accurate, but it completely works (for me lol). Going in, I was comfortable with stream processing and pub/sub from other platforms.
RxJs instantly clicked with me as "almost like embedded redis or kafka".
That thought - again, not completely accurate - lent me almost nil learning curve.
Interesting! I think for those familiar with Kafka that can work well. At least to understand the stream like nature of it.
Good read! Thanks!
Thanks for the nice explanation!
So glad it was useful for you!
Analogy was good.
Great article, thanks!
Glad you enjoyed it!
Thanks, this is super helpful 😎
So glad!
This read makes RxJS more interesting.Thanks for this !!