DEV Community

This Dot Media
This Dot Media

Posted on • Originally published at labs.thisdot.co on

Basic RxJS Operators and How to Use Them

In our Getting Started With RxJS article, we briefly mentioned Operators. In this article, we will expand further on what Operators are in RxJS. We will also show you some basic Operators, what they do, and how you can use them.

What are RxJS Operators?

Taken straight from the RxJS manual:

Operators are the essential pieces that allow complex asynchronous code to be easily composed in a declarative manner.

If you're scratching your head, don't worry. I think most people would be confused by that statement alone. Luckily for us, the manual gives an even better definition:

An Operator is a function which creates a new Observable based on the current Observable. This is a pure operation: the previous Observable stays unmodified.

Essentially, an Operator is like a machine that takes an Observable as an input, performs some logic on the values streamed through the Observable, and creates a new Observable with these values, without changing the original Observable.

The diagram below might help illustrate it a little better.

Operator Explanation

We can see that the Operator takes in the values from one Observable, and creates a new Observable that emits altered values of the original Observable's values, without affecting the original Observable.

Now let's take a look at 6 basic Operators: of, from, map, tap, switchMap, take.

1. of - Creation Operator

The of Operator is a creation Operator. Creation Operators are functions that create an Observable stream from a source.

The of Operator will create an Observable that emits a variable amount of values in sequence, followed by a Completion notification.

A Completion notification tells the Observable's subscribers that the Observable will no longer be emitting new values. We will cover this in more detail in a future article!

Let's take a look at of in practice.

const arr = [1, 2, 3];

const arr$ = of(arr);

arr$.subscribe((values) => console.log(`Emitted Values: `, values));
Enter fullscreen mode Exit fullscreen mode

of creates the Observable, and when we subscribe to it, it starts emitting its values immediately.

The output of the above is:

Emitted Values: [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

of will emit the full array [1, 2, 3] as a full value. This is in contrast to from, which we will look at next!

2. from - Creation Operator

The from Operator turns an Array, Promise, or Iterable, into an Observable.

This operator will convert a Promise to an Observable, allowing for it to be handled in a more reactive manner. When the Promise resolves or rejects, a completion notification will be sent to any subscribers.

Also, unlike of, it will emit each element in an Array or Iterable in sequence, rather than the full value. Once all elements of the Array or Iterable have been emitted, a completion notification is sent to any subscribers.

Let's take the example we used for of to see this difference in action:

const arr = [1, 2, 3];

const arr$ = from(arr);

arr$.subscribe((values) => console.log(`Emitted Values: `, values));
Enter fullscreen mode Exit fullscreen mode

Its output is:

Emitted Values: 1
Emitted Values: 2
Emitted Values: 3
Enter fullscreen mode Exit fullscreen mode

As we can see by the multiple logs, the from Operator took each number and emitted it as a value. The subscriber received each value in sequence, and called console.log three times.

We can also use a value such as a string:

const fromString$ = from("Hello");
fromString$.subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

The output is:

Emitted Values: H 
Emitted Values: e 
Emitted Values: l 
Emitted Values: l 
Emitted Values: o 
Enter fullscreen mode Exit fullscreen mode

How about for a Promise? Let's take a look!

const examplePromise = new Promise((resolve, reject) => {
  // Do some async code and resolve and object with an id property
  return resolve({ id: 1 });
});

const promise$ = from(examplePromise);
promise$.subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

The output of this would be:

Emitted Values: {id: 1}
Enter fullscreen mode Exit fullscreen mode

When the Promise resolves, the value gets emitted as the next value in the Observable.

3. map - Transformation Operator

The map operator is a Transformation Operator. It takes values from one Observable, transforms them, and creates a new Observable that emits the transformed values.

With map, you can perform simple transformations to the values emitted by an Observable. Let's take a look at two examples.

For the first example, we'll take the Array example for the from Operator, and modify it to also use map:

const arr = [1, 2, 3];

const fromArr$ = from(arr);

fromArr$
  .pipe(map((value) => value + 10))
  .subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

You'll notice the introduction of the .pipe() call. This is RxJS's method for applying operators to an Observable's stream before you subscribe to it. It will pipe the value emitted from the Observable through each operator passed as an argument, before passing the final transformed value to the subscribe method. We'll cover this in more detail in a future article!

In this example, as map is a transformation Operator, it must be used within the .pipe() call so that it can transform the value it receives from the Observable. We are simply adding 10 to the value, and emitting the transformed value.

You can see this in the output:

Emitted Values: 11
Emitted Values: 12
Emitted Values: 13
Enter fullscreen mode Exit fullscreen mode

We can do almost anything in the map Operator, but a common use-case would be to get a property from an object that is emitted in an Observable stream. We can use our Promise example to see this in action:

const examplePromise = new Promise((resolve, reject) => {
  // Do some async code and resolve and object with an id property
  return resolve({ id: 1 });
});

const promise$ = from(examplePromise);
promise$
  .pipe(map((obj) => obj.id))
  .subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

Here, we are telling the map operator to return the id property of the object that is resolved in the Promise. The output of this is:

Emitted Values: 1
Enter fullscreen mode Exit fullscreen mode

The map Operator is a commonly used Operator and is very useful for a number of use-cases!

4. switchMap - Transformation Operator

The switchMap operator is another transformation Operator.

switchMap receives the values emitted by an Observable, and then returns a new Observable from a different source.

Let's say you have an Observable that emits User IDs. You may want to fetch the full User object correlating to the ID, then do something with the full details. The switchMap operator would receive the ID from the Observable, then return an Observable containing the response from the request to fetch the User object.

I find it can be useful to think of this in the terms of switching streams. You're switching from one Observable stream to another.

Let's take a look at an example:

const userDetails$ = from(this.userService.getActiveUserID())
    .pipe(switchMap(id => this.userService.fetchUserForID(id)))
    .subscribe(user => console.log("Found user ", user));
Enter fullscreen mode Exit fullscreen mode

Here, we ask for the active user's ID. Then, we ask the userService to make an ajax request to our backend to fetch the User that correlates with the ID. We are assuming that the fetchUserForID call returns an Observable. (This can be possible with the ajax operator which we will discuss in a future article!)

We then subscribe to this new Observable stream, and receive the value it emits, rather than the values emitted from from(this.userService.getActiveUserID()) as seen in the output:

Found user {id: 1, name: "Test User", email: "test@test.com"}
Enter fullscreen mode Exit fullscreen mode

It's worth noting that the switchMap operator will cancel any in-flight network requests if it receives a new value from the original (commonly known as the source) Observable stream, making it a great candidate for typeahead search implementations!

5. tap - Utility Operator

The tap Operator is a Utility Operator which is very similar to a helper function, except in the reactive programming landscape.

tap allows you to perform actions or side effects on an Observable stream without modifying or altering the original stream. The values "pass-through" the tap Operator to the next Operator or Subscriber.

This can be very useful for logging:

const arr = [1, 2, 3];

const fromArr$ = from(arr);

fromArr$
  .pipe(tap((value) => console.log("Received value: ", value)))
  .subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

Which would output:

Received value: 1
Emitted Values: 1

Received value: 2
Emitted Values: 2

Received value: 3
Emitted Values: 3
Enter fullscreen mode Exit fullscreen mode

6. take - Filtering Operator

The take operator is a Filtering Operator. Filtering Operators allows you to select how and when to accept values emitted from Observables.

take is one of the most common and most simplistic filtering Operators. It allows you to specify a maximum number of values you want to receive from an Observable.

We can use our from example where we emit the elements of an Array, and combine it with take to get a better understanding of this operator:

const arr = [1, 2, 3];

const fromArr$ = from(arr);

fromArr$
  .pipe(take(1))
  .subscribe((value) => console.log(`Emitted Values: `, value));
Enter fullscreen mode Exit fullscreen mode

From the output below we can see we only received and used 1 value from the array:

Emitted Values: 1
Enter fullscreen mode Exit fullscreen mode

It can be used in situations where we want to limit how many user-produced events (fromEvent) we want to handle, for example, the first time the user clicks in our app.

Conclusion

In this article, we briefly covered some of what I would consider to be the most common Operators that live in RxJS. By understanding these 6 Operators, you are on your way to mastering RxJS! Stay tuned for more articles discussing more operators and more in-depth topics based on RxJS.


This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit thisdotlabs.com.

This Dot Media is focused on creating an inclusive and educational web for all. We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit thisdot.co.

Latest comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.