The goal of this post is to show how to fix a few common anti patterns I’ve seen in rxjs code.
I should point out that I don’t claim to be an rxjs expert by any standard. However, I have at least played with it enough to have formed an opinion on different patterns I’ve seen evolve.
Initially I found rxjs to be incredibly confusing, but also fun at the same time. I think my proudest moment was when I realized that rxjs is not magic! On the surface it seems very complicated, but it boils down to a relative simple concept at the end of the day.
The API is enormous (lot’s of operators), but only a subset is needed to be productive.
It took me a while to get behind observables. At first glance it seemed like just another paradigm for dealing with (a)synchronous event processing. We already had promises for that, so I was hesitant to embrace this new “toy”.
There a definitely a few cut and dry use cases where observables should always be favored over promises. You shouldn’t be fooled into believing that observables are always better than promises though. There are definitely several cases where both perform equally well.
To be fair, I can’t think of a scenario where promises are far better than observables though. It’s usually a tie game at best.
One of the challenges though is understanding how to properly structure observables. It does sometimes feel like we are going through a similar phase as when we first learned to use promises.
I am certainly not proud of the first promise based functions I wrote. The same is true of my first observables!
In the following section I will highlight a few “anti patterns” I’ve seen in my own, and other developer’s code. I put “anti patterns” in quotes here to not come across as too “preechy”.
Please don’t consider the advice in the following section as the only proper way to work with rxjs/observables. It’s only meant as friendly advice based on personal experiences, and recommendations I’ve seen from knowledgeable rxjs people.
I generally like to subscribe to observables using a single “subscribe” call. I find that a lot of “beginners” end up nesting multiple subscriptions.
This pattern is a clear flashback to improperly chained promise code. Maybe you agree?
Take the example below:
Here I am making two http calls. The second call depends on data from the first call.
However, instead of chaining the calls together, I am making the second call from within the subscribe call of the first one. Generally this will return the correct result, but I find this pattern to be very error prone and a recipe for timing errors.
Instead you should chain the two calls together using an operator like flatMap (see the code sample below).
With this approach the two calls are properly chained together with a single subscription.
This also enables us to add common error handling for both calls.
In the previous example I showed how you can have “too many” nested subscriptions. It turns out you can also have “too many” parallel subscriptions.
See the example below:
Here I have two very similar observables, managed via separate subscriptions. Wouldn’t it be better if we could combine these? It turns out we can if we make a few minor tweaks.
By merging the two observables we can reduce it to a single subscription like so:
Here I’ve used the merge operator to combine the original observables, into a third observable, that I subscribe to. Again, this translates into fewer subscriptions/error handlers to manage.
What if there are side effects?
When subscribing to an observable, it’s very common to set some side effect in the subscribe block.
How does this work in a merged observable?
We may want to update two different properties depending on which observable the values originated from.
Does merging the observables complicate that?
Luckily the answer is no :-)
All we have to do is tack on a “do” operator to each individual observable before merging. This gives us full control over updating properties based on different observables.
See the example below: