How to learn more about RxJs operators?

In the past weeks, we covered a few different RxJs operators in this newsletter. RxJs is the most difficult thing to learn about Angular, and that’s because the technology is a mix of concepts that are not always obvious on their own: asynchronous data and callbacks, functional programming, streams, etc.

My favorite resource to learn more about RxJs is to use a website called rxmarbles.com. Why? Because that website features interactive diagrams of RxJs operators.

Let’s take an example with mergeMap. Here’s the official definition of mergeMap:

Projects each source value to an Observable which is merged in the output Observable.

Rxjs.dev documentation

And here’s an interactive diagram from RxJs marbles for mergeMap – I just recorded my screen while interacting with the diagram by dragging data around:

They say a picture is worth a thousand words. An interactive picture is even better, and that’s exactly what rxmarbles.com is. No text, just interactive diagrams.

Note: Not all operators are documented on that website, just some of the most useful ones. At times, the operator’s signature or name is slightly different depending on the version of RxJs you’re using. Still, overall, RxJS Marbles remains one of the very best tools for understanding operators.

RxJs tap operator

The tap operator is one of the simplest RxJs operators because it doesn’t change anything to the data coming into your observable:

Why would we need a function that doesn’t do anything? We saw a first very important example that enabled us to use the async pipe earlier.

At least three important use cases are:

  • Debugging complex RxJs scenarios using console.log (as illustrated in the above marble diagram)
  • Registering side effects as the data changes (that’s what we did in our async pipe scenario)
  • Watching for completion of inner observables (when you’re combining several observables into one subscription)

In short, tap is a way to spy on what is happening inside an observable stream. Any time you want to extract some data/debug/take a look at what’s going through an Observable, tap is the easiest answer.

You can find examples for these three scenarios in my tutorial: 3 reasons to use the tap operator.

RxJs combineLatest operator

combineLatest has a name that says it all: The operator combines the latest values from two or more observables into one observable.

This marble diagram is the perfect illustration of that behavior (click on the image to access an interactive version):

There are several possible use cases for that operator. The most common usage is filtering information by combining different sources, as illustrated in this tutorial on dynamic filtering with RxJs and Angular forms, which implements the equivalent of an auto-complete text input that displays new suggestions every time the user types new characters in.

What we get out of combineLatest is an array of all the latest values emitted by the combined observables. For instance, if we subscribe to combineLatest(obs1, obs2, obs3), the data we get is an array that contains [lastValueFromObs1, lastValueFromObs2, lastValueFromObs3]. The order of the data in the array matches the order in which we pass the observables to combineLatest, and that array of values will be emitted again every time one of the observables emits again.

A common gotcha of combineLatest is that the operator waits for all observable sources to emit at least one value before returning something, so it can be helpful to use it alongside the startWith operator, as illustrated in this example from our earlier tutorial.

Note: The operator is going to be renamed to combineLatestWith in RxJs 8+.

RxJs switchMap operator

A common mistake made in Angular applications is to nest observable subscriptions in the following manner:

observable1.subscribe(data => {
   observable2.subscribe(otherData => {
     // Do something with otherData
   });
});Code language: JavaScript (javascript)

The above syntax is not recommended because it is hard to read and can lead to subtle bugs and unexpected side effects. For instance, that syntax makes it difficult to unsubscribe properly from all these observables.

Also, if observable1 emits more than once in a short amount of time, we might want to cancel the previous subscription to observable2 and start a new one based on the new data received from observable1. The above code does not do any of that.

The solution: Use the RxJs switchMap operator like so:

observable1.pipe(data => {
   switchMap(data => observable2)
).subscribe(otherData => {
     // Do something with otherData
});Code language: JavaScript (javascript)

The switchMap operator does all of the following:

  • Automatically cancel and unsubscribe from observable2 if observable1 emits a new value.
  • Automatically unsubscribes from observable2 if we unsubscribe from observable1
  • Makes sure that observable1 and observable2 happen in sequence, one after the other.

The following marble diagram is a good illustration of what switchMap does:

If you’re not using switchMap yet, look in your code for nested calls to .subscribe(), and you can start replacing those with the switchMap operator to prevent memory leaks and bugs and make your code more readable.

Here is a link to an example that uses a timer to make HTTP requests every 30 seconds to get updated data. The timer data is turned into an API call using switchMap on line 37 of the example.