Blog

How I Make Sense of RxJS

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

The subscription is raising that drawbridge

The subscription is passed a person, our observer

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 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!

Categories: Blog

Tags: , ,

Laurie Barth
01 Oct, 2019