If you like “promises”, you’ll love “observables”.
“Observables” are “something” that you can subscribe to and from which you’ll receive values over time. I’m sure everyone knows what is a “rsa securId” but for those who don’t know, it’s a little device that displays a random access token that changes every X minutes. You can use this token to log in an application such a banking web site. We can imagine that the the device is actually an observer of the observable emitting the access token every X minutes.
I know that this metaphor is probably the best you’ve ever heard (or the worse), but let’s bring it back to the web development context and let’s see how to use “RxJS“, the famous Reactive programming library.
Well, that’s probably the easiest part of this post. “RxJS” can be used by installing the corresponding “node” package.
yarn add rxjs -S
Use built-in observables
Before creating our own observables, we’ll start by using the one “RxJS” provides for us. In this example, we’ll simply display the location of the mouse when it stopped moving on the page. So the goal is not to display the cursor location as soon as it moves but when it stopped moving.
Let’s start with the HTML:
Then, we’ll subscribe to an observable bound to the “mousemove” event of the “document” object. So basically, this observable will emit a value every time the “mousemove” event of the “document” object is triggered.
To subscribe to an “observable”, we just need to call the “subscribe” method and pass an object with some specific properties:
- next: callback function called when a value is emitted by the observable.
- error: callback function called when an error is emitted by the observable.
- complete: callback function called when the observable completes.
So basically, it looks like this:
“RxJS” also allows you to pass these callbacks directly as arguments:
The three arguments are optional (even though, I’m wondering what would be the use of an observer with none of the three callbacks defined).
It’s possible to get an “observable” pretty easily with “RxJS”, for example, we’ll get an observable for the “mousemove” event of the “document” object by using the “fromEvent” function like this:
The line 1 imports “fromEvent” from “rxjs” library while the line 2 retrieves a reference to the “span” that will display the cursor location (don’t even think using jQuery to get this reference). The main piece of code starts at line 5 where we use “fromEvent” to get an observable for the “mousemove” event (second argument) of the “document” object (first argument). Once we get it, we need to subscribe to it in order to be able to get the values it will emit. We’re using the second form of the “subscribe” function that we saw above where the “next” callback is defined as the first argument of the function.
This callback will receive an argument that will be the value emitted by the observable. So, every time the cursor will move on the document, the callback will be called with a specific value in argument, in this case, it will be a “MouseEvent” object that contains two interesting properties:
- clientX: Position X of the cursor.
- clientY: Position Y of the cursor.
Our code uses these two properties to display the cursor position on the page.
Sometimes, it can be interesting to perform some actions on the “observable” before we receive the emitted value. For example, in our code above, we will receive a “MouseEvent” object in argument that contains a lot of properties but we will only be using the “clientX” and “clientY” ones. It would be cool to receive a simpler object that only contains a “x” and a “y” property.
To do so, we can use the “pipe” function to apply some transformations to the emitted value. Let’s use the “map” operator to convert the “MouseEvent” object to the simpler object that we just talked about:
The “pipe” function takes a indefinite number of parameter being functions receiving the emitted value. In our case, we use the “map” function to convert a value in another one. Here, we receive our “MouseEvent” object and we simply return a new object with a property “x” that contains the property “clientX” of the emitted value and a property “y” that contains the property “clientY” of the emitted value. This means than in the callback of the “subscribe” method, we will no longer be getting a “MouseEvent” object but an object with an “x” and a “y” property.
Cool, huh? However, there is something wrong with our code… We said that we’ll display the cursor position once it stopped moving but the position is refreshed as soon as the cursor moves. To solve this, we’re going to use the “debounceTime” operator of “RxJS”. Basically, this operator will ignore the emitted value as long as the time interval between the last emitted value and the present moment is shorter than the specified value. Let’s see how to use it, then we’ll make this explanation clearer:
We added the call to “debounceTime” in the “pipe” function of the observable, this way, it will be called for every emitted value. As we said, this function will ignore the emitted value as long as the time interval between the last emitted value and the present moment is shorter than the argument passed to this function.
If you test this code, you’ll see that as long as the cursor is moving, the location in the “span” is not refreshed. This is because when you move the cursor, a new location is emitted for every pixel and unless you move your cursor veeeeery slowly, there will be less than 100 milliseconds between two different locations, so the value will be ignored. Then, at some point of time, you’ll stop moving your mouse and 100 milliseconds after this, the value will be emitted, so the position will be refreshed.
This operator is also very useful for an auto complete component. Most of the time, you don’t want to search for what the user is typing directly after he typed a letter, because when he wants to search “videos of cute and fluffy cats”, you don’t want to make a search for “v”, then “vi”, then “vid”,… It’s better to do the research when the user seems to have stopped typing its search criteria.
Come on, stop this!
It is as easy to unsubscribe from an observable as it is to subscribe to it. What we didn’t say here is that the “subscribe” function actually returns an object that is called a “subscription”. You can call the “unsubscribe” method on this object to stop listening to the emitted value. Let’s modify our “HTML” like this:
Et voilà, you just added a “click” event listener to the button and when it is fired, we call “unsubscribe” on the object returned by the “subscribe” function of the observable, so when you click on the button, the position of the cursor is not refreshed anymore. It’s always a good practice to unsubscribe from an “observable” that can potentially never completes.
I want mine, I want mine !
Using built-in “observables” like this is already pretty cool but it’s even cooler to create yours. We’re going to see how to do this by creating our own small “rsa-securid” application. So basically, we’ll have a module called “rsa” that will be responsible for generating “rsa” tokens every 2 seconds (because it would be boring to test if new tokens were emitted every 5 minutes). So start by creating the module “./src/rsa.js”.
We’ll be creating “observables” by using “RxJS” subjects. A subject is an object on which you’ll be able to call three main functions:
- “next” to emit a new value on the “observable”.
- “error” to emit an error on the “observable”.
- “complete” to indicate that the observable is completed.
These functions ring a bell? It’s normal, their name are the same as the callbacks that we pass to the “subscribe” method of the “observable”. There are different kind of subjects and the ones I use the most are:
- Subject: Simple subject that emit value.
- ReplaySubject: When you subscribe to an observable bound to a “ReplaySubject”, you’ll get a certain number of value that have been already emitted in the past.
- BehaviorSubject: When you subscribe to an observable bound to a “BehaviorSubject”, you’ll get the last value emitted or a default one if no value have been emitted yet.
Here, we’ll use a “BehaviorSubject” in order to have a token as soon as we subscribe to the observable. So let’s start by importing “BehaviorSubject” from “RxJS”:
Then, implement the function responsible for the token generation. Here, it won’t be rocket science, just a random number between 10000 and 99999 (not even sure, I didn’t tested it thoroughly, but that’s OK 😉 ).
Next, we create our subject:
The “BehaviorSubject” constructor takes a parameter that represents the value that will be emitted upon subscription if no value have been emitted yet. Then we need to emit a new token every two seconds and for that, we’ll use the “next” function of the subject :
And finally, we’ll export a default function that is going to return an observable bound to our subject. To do this, we just need to call the “asObservable” function of the subject.
We are now going to use this module to display the generated token in the page. Update your “HTML” code with this:
And your “index.js” with:
Line 1 imports our “rsa” module, line 3 gets a reference to the “h1” of our page and line 5 subscribes to our “observable” an update the content of the “h1” for each emitted value. Go ahead, test it, you’ll see a new tokens every 2 seconds.
Note that if we had used a “Subject” instead of a “BehaviorSubject”, we wouldn’t see any token the first 2 seconds as a normal “Subject” wouldn’t have emitted a default value upon subscription.
I don’t see the point of error/complete callback 🙁
So far, we just saw how to use the “next” callback, we are now going to see how to use the two others one. Let’s add a stupid rules to our rsa generation stating that even though we generate tokens between 10000 and 99999, if a token is higher than 50000, we wont emit it but we’ll throw an error. It does not make any sense but it’s the easiest I could come up with for the example 🙂
Let’s first implement the error throwing. For this, replace your “setTimeout” function by this one:
So as promised, if the token is smaller or equal to 50000, the subject emits the value, however, if it’s greater than 50000, an error will be thrown through the “observable”. Now we need to catch this error in our “index.js” file:
This time, we’re using the second argument of the “subscribe” method to display the error in the “h1”. Note that if an error is thrown through the “observable”, it stops it. In other words, as soon as you get an error, you won’t get any subsequent values.
Completing an observable is as simple as throwing an error on it, instead of calling “error”, you just have to call “complete”. For example, we would use this code to complete the observable once 10 tokens have been generated:
Then we can catch the “complete” callback this way:
Here, I chose to pass an object as I don’t want to handle the “error” callback, I find it cleaner that way.
So I don’t need promises anymore?
We all know this feeling… We just learn something so cool that we start using it everywhere, even when it is not necessary just because… well… it’s cool. If you never used “observables” before, you will probably tend to have this behavior and you will maybe even think that now that you have “observables”, you don’t need “promises” anymore but that’s not entirely true.
Before making your decision, it’s important to know the differences between “promises” and “observables”:
- Promises are only capable of handling one event and are natively not cancellable. Take the example of an “HTTP” request. You execute it and then you’ll receive a “promise” that will get executed as soon as the “HTTP” call response is there. You can’t cancel this call and once you receive the response, you can’t do anything else with your promise.
- “Observables” can handle zero, one or more events. Take the example of a web socket connection. Once you’re connected to it, you can potentially receive an infinite number of messages, so “observables” are a perfect fit to handle this.
However, it’s not because “observables” can do everything that “promises” do that you have to use “observables” everywhere. I would advice to continue using “promises” in the case described above: you only expect one value and you don’t need to cancel the promise or apply some operators (such as “map” or “debounceTime”) on it.
This post only introduces a tiny part of what “RxJS” is. Indeed, I didn’t talked about the difference between “cold” and “hot” “observables”, I took the easy way out by using “RxJS subjects” and I only talked about two operators while it exists dozens of them. However, as the post title states, this was just an introduction to “RxJS”. If you like what you just read here, I’d advice you to dig deeper and learn more about “RxJS” because this is really awesome.