Hackerino

How to start using RxJS

ReactiveX has revolutionized the way modern developers think about building apps. It allows us to treat all data as a stream as it unfolds through the dimension of time.

In this lesson, I'm gonna cover about 25 different ways you can use RxJS so you have a solid foundation to use it in your own projects.

For this tutorial, I've just created a basic HTML page and imported the RxJS script from a CDN in the head of that page. Then we'll write all of our custom code in this lesson.js file and import that in the body of the page. Then inside lesson.js, I'm creating a helper function that will just append the results from an observable to the browser on the right side here.

Observables

The first thing we're gonna talk about is the observable. And I think the easiest way to think about it is just an array that gets built over time. Then you can loop over this array in the dimension of time by subscribing to it. The first thing we're gonna do is create an observable from scratch. We do this by calling rx.observable.create and then create takes a observer function where we can define what the observable sends to the subscriber.

// RxJS v6+
import { Observable } from 'rxjs';

const hello = Observable.create(function(observer) {
  observer.next('Hello');
  observer.next('World');
  observer.complete();
});

To send values, you call observer next with the value you wanna send. And that value can be anything. It could be a string, an object, or whatever you want. Then to make the observable start emitting values, you call subscribe on it. In this case, we'll take each value and just print it to the screen.

// RxJS v6+
import { Observable } from 'rxjs';

const hello = Observable.create(function(observer) {
  observer.next('Hello');
  observer.next('World');
  observer.complete();
});

const subscribe = hello.subscribe(val => console.log(val));

If we refresh our page, you can see we get hello world printed to the screen. So that's pretty cool, but we don't normally need to create observables manually like this. Let's say we wanted to create an observable from click events in the DOM. We can call observable from event, pass it the document and the event we wanna observe, in this case clicks.

So just like before, we subscribe to it, but this time we'll console log it just to show the full event object. Each time we click the page, we get the mouse event logged along with all of its associated data. If you work with asynchronous JavaScript, you're probably used to working with promises and we can convert a promise directly into an observable. So here we're just creating a promise with a timeout that resolves after one second.

The idea here is to simulate an API call or some other asynchronous operation. To convert this promise to an observable, we just call observable from promise and pass it the promise. This is extremely useful when you're working with a JavaScript library that's built on promises. You can also convert the observable back to a promise by calling to promise on it.

So when we subscribe to this observable, we get a one second delay and then it prints resolved on the page. We can also create a timer by calling observable timer with the amount of milliseconds we wanna wind it up for. To start the timer, we just call subscribe on it and then it will emit a value once the timer runs out. So we refresh the page and one second later, our timer runs out. If we want a timer that keeps repeating, we can use an interval which emits a value after the specified timeframe. So every 1000 milliseconds will get a new value emitted. In this case here, we'll print out a date with the number of seconds in the minute to show how this works. We reload the page and then every second, we get a new value emitted from the observable.

Now I'm gonna show you one last way to create an observable. There's actually more ways than this, but I think this covers the basics for now. So we'll do observable of which allows us to pass any static value we want to the observable. So we'll go ahead and pass it a string, an array, a number, a Boolean and an object. This example shows how any type of data can be a stream, which is an important concept to keep in mind when building reactive software.

Hot vs Cold Observables

The next concept we'll look at is hot versus cold observables. A cold observable is basically just an observable where the data is created inside of it. So this means it won't actually create the underlying data until something subscribes to it. Inside this observable, I'm just generating a random number and then I'll create two different subscribers and you might intuitively think that they should get the same value. But what happens is the random number gets generated once the subscription starts. So each subscriber gets a different number.

That's how you know you're working with a cold observable, but you can make an observable hot by simply building that value outside of the observable itself. So here we set the random number outside the observable and then pass it in as a variable, reload the page and then we'll see both subscribers get the same random number.

That technically makes our observable hot, but there is a way to do this without decoupling the data from the observable itself. So we'll put the random number back in the observable, then we'll create a new hot observable by calling publish on the cold observable.

This tells it only to emit data once we call the corresponding connect method on it. So we'll build the subscriptions and at this point, nothing will happen. Then call connect on the hot observable and that will trigger these subscriptions to emit data and this time it'll be shared between the two of them. And we can verify this by reloading the page and they should get the same random number.

Completing the Observable

The next thing you should know is how to complete an observable. When the observable has reached the end of its life cycle, it will send a completed signal. We can see this with a timer using the finally operator, which will run the code once the observable is completed. In this case, it will be completed after one second. So we subscribe and then we get the all done signal back. However, there are other observables that won't complete automatically. And in some cases, this can result in memory leaks if it's a very data intensive stream. Here we have an interval which doesn't complete on its own. So what we do is subscribe to it first, then set a timeout for three seconds and call unsubscribe. This manually forces the observable to send the complete signal.

There's also more clever ways to do this, which we're gonna see a little bit later in the lesson.

RxJS Operators

Now we're going to look at a whole bunch of RxJS operators starting with map. Map allows you to transform the emitted value based on some underlying logic. In this case, we created an observable of some numbers and we're going to transform them to their logarithm. So when we subscribe, instead of getting the actual numbers, we get the log of that number.

A more practical example for app developers is when you send an API request that responds in JSON. You need to convert the JSON string to JavaScript. So what you can do is use the map operator to run JSON parse on that string from the API. Then the observable will emit JavaScript objects that you can actually use in your app. The next operator we're going to look at is do. This will allow us to execute code without affecting the underlying observable. So we create an observable of a couple of strings and then we'll print out their names as they first appear. Then we'll transform them to uppercase and then we'll print them again. When we actually run this code, you can see that it gives us a glimpse into the observable data at any given point in time. So here we see the original value and the uppercase value. Now let's take a look at filter. This does pretty much what you'd expect. You give it a condition and only values meeting that condition make it through. So here we have an observable of a bunch of positive and negative numbers and we'll filter it to only show the positive numbers. So only values greater than or equal to zero will be displayed and we reload the page and we only see the positive numbers. If we flip greater than to less than, then we get only the negative numbers displayed. Using this same example, I'm going to show you the first and last operator. The first operator will just take the very first element from the observable. In this case, the first element is negative three so that's what we see here on the screen. We can also do the inverse of this with the last operator and negative two is the last element so we'll see it updated to negative two. Now let's take a look at debounce and throttle. These two operators allow you to deal with events that emit way more values than you actually need. The mouse move event is a perfect example in the DOM so we'll subscribe to the mouse event and then we'll print the type of event here on the screen. Let's first do this without any throttling or debouncing. You can see it only takes about two seconds to get hundreds of events printed here on the screen. We can use throttle to get the very first event and then set a delay before it starts emitting any other events. In this case, we'll use the throttle time helper and pass it 1000 milliseconds. So now if we mouse over the screen, we'll get the first event and then it will wait at least one second before sending any additional events. Debounce does almost the same exact thing but instead of giving us the first event, it gives us the last event. So first we mouse over and there's a delay. Then after that delay, it will emit the most recent event. Debounce is especially useful when you wanna make sure the user has stopped doing something such as typing in an autocomplete form or something similar. Now we're going to move on to the scan operator. This one seems confusing at first but it's actually very simple. We're going to do this in the context of a video game. So first we're gonna subscribe to the click events in the browser. And then for each click, we're gonna assign a random number that's gonna represent the score for that click. Then we'll use the do operator to print that score on the screen. And then we'll use scan to keep a running total of the high score, which is just the combined score of all the clicks. So scan is just keeping a running total of each emitted value from the observable. It works just like the array reduce function in vanilla JavaScript. So we'll go ahead and print out the high score here and we should see it get increasingly larger with each additional click. So on the first click, the high score and the click score are the same. And then the high score gets increasingly larger with each additional click. Now let's move on to switch map. This operator is especially useful when you have one observable that you need a value from before you can get a second observable. In this example, we have an observable of clicks but we actually don't care about the click data. We want to start an interval timer after each click. So on the first click, the interval should start. And then when we get a new click, it'll reset the interval back to zero. So we'll go ahead and click the page and then we'll let the interval count up to six and then click again and it'll reset back to zero. Switch map is commonly used in app development when you have an observable of say a user ID and then you need that user ID first before querying a database for that user's data. The next operator is take until. This one allows us to complete an observable based on the value of another observable. So here we have an interval and a timer. The interval is going to emit values every 500 milliseconds until the timer runs out after two seconds. This is a clever way to unsubscribe from data without actually having to call unsubscribe on the observable itself. So here we get the first few emitted values, then the timer runs out and so it logs complete. A closely related operator is take while. This one will tell the observable to emit values until a certain condition turns true. In this example, we have a collection of names and we want to stop the observable once it reaches the name of Doug. In other words, we want it to take values while the name does not equal Doug. Then we'll use the finally operator to verify that the observable completed successfully. So when we run this in the browser, we can see we only get the values that came before Doug. Now let's look at a few ways we can combine observables, starting with zip. Zip is something that might be useful if you have two observables that are the same length and connected in some kind of way. So here we have observables yin and yang and we're going to combine them with zip, which will combine them into arrays based on their index position in the observable. So peanut butter will be matched with jelly, wine with cheese, rainbows with unicorns. Another way to merge observables is with fork join. This one will wait for both of the observables to complete and then it will combine the last two values together. So I'm going to put a delay on the yang observable. Then we'll use the fork join operator to combine them just like zip. But this time we should see a two second delay and then only the last two values, rainbows and unicorns, should be emitted. This operator is useful if you have a bunch of related API calls and you want to wait for all of them to resolve before sending any data through to the UI. Now we're going to look at catch for handling errors. In this example, I create a new observable from scratch, but in the middle of it, I throw an error. When we get errors in the real world, we want to be able to catch them and then do some error handling. In this case, we'll use the catch operator to complete the observable and just print out the error. So notice that we get the first three values in the observable, but the very last one doesn't get emitted. Overall, the idea is very similar to how promises work in JavaScript. We also have a useful retry operator that will rerun the observable as many times as we want when an error has occurred. So right after catch, we can add retry. And in this case, we'll retry it twice. When we run it this time, we'll get the same sequence of events, but they'll happen three times. The initial run plus the two retries. So that's all the operators I'm gonna cover for today. There's tons more in RxJS, but I think these are some of the most useful and powerful ones that you'll find. We just have one more topic to cover and that's the RxJS subject. A subject is just an observable with a few extra bonus features. It has the ability to emit new data to its subscribers by acting as a proxy to some other data source. This will make more sense after the next couple examples. So here we have a plain observable and we're going to convert it to a subject. When we subscribe to it, it emits the values just like a normal observable would. But the real benefit of subject is to be able to call next on it. So what we do is create a new subject and then we'll create a couple subscriptions to it. And then we can just call subject next with some value. That's not something you can do on a normal observable. Then we'll set another next call inside of a timeout. So the main benefit of a subject is being able to broadcast new values to subscribers without having to rely on some source data. To build on this topic, we're gonna look at one last thing and that's multicast. It's used to send values to multiple subscribers but not any related side effects. So we create a data source that is the click events happening in the DOM and then we use the do operator to print do one time. This is an example of a side effect. Imagine we had a hundred subscribers, it would run this code for every single one of them. So if you had a hundred clicks and a hundred subscribers, this code would run 10,000 times. What we actually want is for it to only run on the 100 clicks coming from the source observable. We can do this by calling multicast on the original observable and have it return a new subject. Then we can add a couple of subscriptions to the subject. Then lastly, we need to call connect on that subject. When we start clicking in the DOM, we'll see that we get the values sent to the subscribers and the side effect only runs once even though there's multiple subscribers. This could be highly beneficial if you have multiple subscribers to a single data source that is running some sort of side effect. That's it for my RXJS quick start. If this video helped you, please like and subscribe. And if you wanna get the full source code and additional commentary, head over to angularfirebase.com. Thanks for watching and I'll see you next time. Thanks for watching.