5 min to read
RxJS!
RxJS
Well, going back a little to what we have been seeing of reactive programming with RXJS, let’s see in detail some operators to begin to know and understand them to begin to apply them. Remembering the concept of stream, as a collection of future data/events.
Reviewing briefly the previous post, we saw that we can apply different operators to transform/manipulate our stream, and they are classified by category. There are a lot of them, so let’s see some of them
Create operators
Create
We create an obs that broadcasts hello and friends when you subscribe with create:
import { `Observable` } from 'rxjs';
const hello = Observable.create((observer) => {
observer.next('Hello');
observer.next('Friends');
});
const subscribe = hello.subscribe(val => console.log(val));
From
We transform a collection/array or a promise into an observable with from:
import { `from` } from `rxjs`;
const arraySource = from([1, 2, 3, 4]);
fromEvent
We convert any type of event, e.g. mouse move, into an observable with fromEvent:
import { fromEvent } from 'rxjs';
const source = fromEvent(document, 'mousemove');
source.subscribe(e => console.log(e.clientX + ', ' + e.clientY));
Filtering operators.
With these operators we are going to filter (yep just like the array filter), to accept or reject certain values according to the filter criteria.
Filter
Only emits values if it meets the given condition.
import { from } from 'rxjs';
import { filter } from 'rxjs/operators';
const numbers = from([2, 30, 22, 5, 60])
numbers
.pipe(filter(n => n > 10))
.subscribe(n => console.log(n))
Mental note: Every time we are going to apply operators, we write .pipe(), to indicate that the chain of operators that we want to apply is coming, yes, infinite without limits.
Transformation operators
Map
Transforms the elements emitted by an observable, applying a function to each one, in this case we multiply by 10 each one of the elements:
import { from } from 'rxjs';
import { map } from 'rxjs/operators';
const numbers = from([1, 2, 3, 4])
numbers
.pipe(map(x => 10 * x))
.subscribe(n => console.log(n))
Reduce
Map, reduce, all the same as in JS Arrays. We apply a function to an accumulator and to each value to reduce it to a single value.
import { from } from 'rxjs';
import { reduce } from 'rxjs/operators';
const numbers = from([1, 2, 3, 4])
numbers
.pipe(reduce((x, y) => x + y))
.subscribe(n => console.log(n))
Higher-Order Observables
And here we start to have some fun! The magic of the operators comes into play when we begin to understand their differences and the control of the observable inner/outer. wait whaaaaat?
Suppose we have an observable with 2 numbers:
of(3, 7) //don't forget the import of of from rxjs :P
And those numbers are actually the IDs we need to retrieve data from a product:
of(3, 7)
.pipe(map(id => this.http.get<Supplier>('${this.url}/${id}')))))
.subscribe();
Then the first number observable is the outer Observable. Each time one of these numbers is emitted, it maps to a request that returns another observable. Now if this is the inner Observable. So yes, we have observables emitting observables!!!
Now the first thing we would think is, in order to subscribe to the inner and consologue the product data, we would have subscribes nulled (Oh no!), because remember we already have the subscribe of the outer (above), and without subscribes there is nothing (previous post, the lazy bums). So how do we do, subscribe hell?
We use the magic operators, the observable high-order! They subscribe and unsubscribe themselves from the inner ones. How can we recognize them? Just by having the word “Map” at the end of their names ;) The same example above works for all of them, just changing map for the operator of each section!
concatMap
This operator WAITS for each inner to complete BEFORE continuing and processing the next one. So it concatenates the result of the inner ones in a sequence.
*The idea is to use concatMap when you want to make sure that each inner is processed one at a time and in order. This technique is great for updating or deleting data, ensuring that each operation is processed sequentially.
mergeMap/flatMap
With this little fella the magic happens PARALLELIZAAAAA. Yes! It is a high-order operator that processes the inner ones in parallel. Then it merge the results of each one. So when it finishes with all the inner, it returns the result of the stream. And no, it doesn’t respect the order like the previous one, the one that arrives arrives arrives, or the one that went to the village lost his chair?
*Use mergeMap for better performance (parallelize) as long as you don’t care about the order.
switchMap
Just what the switch does is to switch between the inner ones! Following as always the example above, but using switchMap, we can see that as soon as the first value of the outer is emitted, it subscribes to the first inner. When it emits the value, it unsubscribes from the previous one and subscribes to the next inner. Each time an inner completes, it emits each result to the stream result. When they all finish, the stream result is completed.
*Use switchMap to stop the inner BEFORE processing the next one. Let’s imagine a type ahead or autocomplete, when you want to stop processing when the user types the next character.
Bonus!
Yessssss there are bonusssss, 2!
1- Playground obviously to play and experiment a bit with these concepts. https://rxjs-playground.github.io/#/
2- There is an extension for VSCode if you want to have at hand the cheatsheet with the marbles https://marketplace.visualstudio.com/items?itemName=dzhavat.rxjs-cheatsheet