by Tomas Trajan
⚡ How to never repeat the same RxJs mistakes again⚡
Remember: .pipe() is not .subscribe()!
This article is directed at the beginners trying to increase their RxJs knowledge but can also be a quick refresh or a reference to show to beginners for more experienced developers!
Today we are going to keep it short and straight to the point!
Currently I am working in a rather large organization quite a few teams and projects (more than 40 SPAs) that are in the process of migration to Angular and therefore also RxJs.
This represents a great opportunity to get in touch with the confusing parts of RxJs which can be easy to forget once one masters the APIs and focuses on the implementation of the features instead.
The “.subscribe()” function
RxJs observable represent a “recipe” of what we want to happen. It’s declarative which means that all the operations and transformations are specified in their entirety from the get go.
An example of an observable stream could look something like this…
This RxJs observable stream will do literally nothing by itself. To execute it we have to subscribe to it somewhere in our codebase!
In the example above, we provided a handler only for the values emitted by the observable. The subscribe function itself accepts up to three different argument to the handle next value, error or complete event.
Besides that we could also pass in an object with the properties listed above. Such an object is an implementation of the Observer
interface. The advantage of observer is that we don’t have to provide implementation or at least a null
placeholder for the handlers we are not interested in.
Consider the following example…
In the code above, we are passing an object literal which contains only complete handler, the normal values will be ignored and errors will bubble up the stack.
And in this example, we are passing the handler of the next error and complete it as direct arguments of the subscribe function. All unimplemented handlers have to be passed as a null or undefined until we get to the argument we’re interested in.
As we can see, the inline argument style of implementation of a .subscribe()
function call is positional.
In my experience, the inline arguments style is the one which is most common in various projects and organizations.
Unfortunately, many times we may encounter implementation like the following…
The example above contains redundant handlers for both next
and error
handlers which do exactly nothing and could have been replaced by null
.
Even better would be to pass the observer object with the complete
handler implementation, omitting other handlers altogether!
The “.pipe()” and the operators
As beginners are used to providing three arguments to subscribe, they often try to implement a similar pattern when using similar operators in the pipe chain.
RxJs operators, which are often confused with the .subscribe()
handlers, are catchError
and finalize
. They both serve a similar purpose too — the only difference being that they are used in the context of the pipe instead of the subscription.
In case we would like to react to the complete event of every subscription of the RxJs observable stream, we could implement finalize
operator as a part of the observable stream itself.
That way we don’t have to depend on the developers to implement complete handlers in the every single .subscribe() call. Remember, the observable stream can be subscribed to more than once!
This brings us to the final and arguably most problematic pattern we may encounter when exploring various code bases: redundant operators added when trying to follow .subscribe() pattern in the .pipe() context.
Also, we might encounter its even more verbose cousin…
Notice we have progressed from the original single line to the full nine lines of code which we have to read and understand when we want to fix a bug or add a new feature.
Stuff might get even more complex when combined with more complex generic Typescript types, which can make the whole code block even more mysterious (and hence waste more of our time).
Recapitulation
- The
.subscribe()
method accepts both the observer object and inline handlers. - The observer object represents the most versatile and concise way to subscribe to an observable stream.
- In case we want to go with the inline subscribe arguments (
next
,error
,complete
) we can providenull
in place of a handler we don’t need. - We should make sure that we don’t try to repeat the
.subscribe()
pattern when dealing with.pipe()
and operators. - Always strive to keep the code as simple as possible and remove unnecessary redundancies!
That’s it! ✨
I hope you enjoyed this article and will now have better understanding of how to subscribe to RxJs observables with clean, concise implementation!
Please support this guide with your ??? using the clap button and help it spread to a wider audience ? Also, don’t hesitate to ping me if you have any questions using the article responses or Twitter DMs @tomastrajan.
And never forget, the future is bright
Starting an Angular project? Check out Angular NgRx Material Starter!
If you made it this far, feel free to check out some of my other articles about Angular and frontend software development in general…
??️ The 7 Pro Tips To Get Productive With Angular CLI & Schematics ?
Angular Schematics is a workflow tool for the modern web — official introduction articlehackernoon.com The Best Way To Unsubscribe RxJS Observable In The Angular Applications!
There are many different ways how to handle RxJS subscriptions in Angular applications and we’re going to explore their…blog.angularindepth.comTotal Guide To Angular 6+ Dependency Injection — providedIn vs providers:[ ] ?
Let’s learn when and how to use new better Angular 6+ dependency injection mechanism with new providedIn syntax to make…medium.com The Ultimate Answer To The Very Common Angular Question: subscribe() vs | async Pipe
Most of the popular Angular state management libraries like NgRx expose application state in a form of a stream of…blog.angularindepth.com