⚡ How to never repeat the same RxJs mistakes again⚡

Remember: .pipe() is not .subscribe()!

Tomas Trajan
Jan 22, 2019 · 6 min read
Look! A lightning tip! (Original 📸 by Max Bender)

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

An example of an observable stream could look something like this…

Example of the RxJs observable stream declaration

This RxJs observable stream will do literally nothing by itself. To execute it we have to subscribe to it somewhere in our codebase!

This subscription will log our greetings every odd minute

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…

Example of redundant handlers often encountered in the “wild”

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

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!

Use the finalize operator to react to the complete event of the stream independently from the subscription. (Similar to tap)

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…

Stuff might get verbose…

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).


  1. The observer object represents the most versatile and concise way to subscribe to an observable stream.
  2. In case we want to go with the inline subscribe arguments (next, error, complete) we can provide null in place of a handler we don’t need.
  3. We should make sure that we don’t try to repeat the .subscribe() pattern when dealing with .pipe() and operators.
  4. 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

Obviously the bright future! (📸 by Xavier Coiffic)


This is no longer updated. Go to https://freecodecamp.org/news instead

Tomas Trajan

Written by

🅰️ Google Developer Expert for Angular #GDE ❤️ ️Typescript 🛠️ Maker of the @releasebutler and Medium Enhanced Stats 🌞 Obviously the bright Future


This is no longer updated. Go to https://freecodecamp.org/news instead

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade