RxJS Top Ten - Code This, Not That

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] one of the most difficult but also most rewarding javascript libraries to learn is rxjs it's a tool that helps us control data as it flows through the dimension of time in today's episode of code that's notthat you'll learn ten fundamental concepts in rxjs and how to avoid the bad stuff if you're new here like and subscribe and leave me a comment below for a chance to win this t-shirt next week rxjs is getting pretty popular these days in fact it gets more daily npm downloads than angular reactive view and the only functional library that i'm aware of it's bigger is low - and the reason it's so popular is because javascript alone doesn't give us quite everything we would like to work with asynchronous streams of data we have promises but they only work with a single ASIC value so we use callbacks for real time streams but you've probably heard the term callback hell rxjs addresses these issues and gives us a powerful functional library for dealing with strings and when I say stream I'm talking about any data source that unfolds over the dimension of time things like data from firebase Dom events WebSockets file uploads etc the first thing you'll need to do is install rxjs I'm currently running version 6.4 now I'm just using vanilla JavaScript with webpack but feel free to follow along with your favorite JavaScript framework the most fundamental class in rxjs is the observable you can think of it as a wrapper for some data that can be subscribed to and then the subscriber will be notified anytime the data changes when working with rxjs you should think of yourself as a plumber and I mean that in the most literal sense because an observable is essentially a pipe for data and rxjs gives you all kinds of tools to modify those pipes let's go ahead and create our first observable but first we need to import the rxjs library and there's a right and a wrong way to do this currently I'm doing it the wrong way by using import star as our X but that's going to cause the entire library to be bundled up in our code you can see down here we have a bundle size of 47 kilobytes but rxjs is a tree shakable library which means we only have to import the code that we actually need so when working with rxjs you should only import the classes and operators that you actually need in your source code and by doing that we reduce our bundle size to less than 2 kilobytes now that you know that let's go ahead and create an observable from scratch now you won't normally create observables like this in your own code but it's a good exercise to understand what an observable is when we call observable it gives us a callback function that we can use to notify a subscriber with some new data this can be synchronous or asynchronous but basically every time we call next it's like emitting an event to the subscriber that they can listen to and react to now that we have our observable created we can create a subscription to that observable by calling subscribe on it subscribe takes a function that will be called every time the observable emits a new value and this is why they call it reactive programming because with your subscription you're reacting to changes in the observable data throughout this video I'm using my own helper method called print' which will react to each newly emitted item and print it to the Dom so we can visualize what's happening in the observable and if we open this in the browser you can see we get ABC logged one after the other another important concept to keep in mind is that observables can be completed which means they'll be shut off and no longer a net values if we had observer complete and then go back to the browser you can see that only a and B are emitted and that's because this stream has been closed if you think like a plumber creating the observable is like connecting a pipe to a water source and then subscribing is like opening the valve that lets that water out rxjs provides a bunch of helper functions that make it easier to create observables for example if we want to just create an observable of a raw value we can use of this we'll just take the value that you pass in and wrap it in an observable if we subscribe to this then we'll just get hello printed out in the console now the of function is very easy to mix up with the from function which takes an array promise or iterable and then emits each individual item from the observable we can see the difference by passing in a string which itself is iterable instead of emitting the entire string as one event it emits each individual character in that string another thing we can do is create an observable from events in the Dom using from event we first pass in a Dom element and in the event that we want to listen to if we listen to clicks on the document and subscribe to the observable we'll get an event every time we click on the page another thing we can do is set up an observable based on a time interval it takes the number of milliseconds as its argument and then will emit a number each time that interval passes now one thing that a lot of people don't realize is that rxjs can be synchronous or asynchronous and you'll probably never have to do this but you can control that behavior by modifying schedulers if you get lost in this next example I would recommend watching my async/await video which will teach you all about the event loop and JavaScript let's go ahead and define a string observable using of and then we'll just synchronously call our print method with world in this case rxjs is going to treat that observable as asynchronous value so the subscription will happen on the main thread therefore we get hello world printed one after the other you can change this behavior by modifying the schedule or of the observable if we pass in the async scheduler it will emit the value of the observable on the next iteration of the event loop this time in the browser we get world hello and again you'll probably never need to mess with that but I just want to give you an idea of what schedulers do the next thing we'll do is look at the difference between hot and cold observables I don't really like this terminology and I think the best way to think of it is hot observables can have multiple subscriptions whereas cold observables can only have one subscription cold observables don't actually create the underlying value until they're subscribed to will go ahead and create an observable from scratch and just have it generate a random number now if we subscribe to this observable twice you'll see that we get two different random numbers that's because that create callback function isn't called until the subscription is created but often in the real world that's not what you actually want a lot of times rxjs is valuable because you can share a value across multiple subscribers if you have an existing cold observable there are multiple ways to make it hot or in other words allow it to be broadcast to multiple subscribers now in this example only the first subscriber will get the actual value but what's often useful in the real world is to use the share replay operator which will cache the last value as well if instead we pipe and share replay one you can see that both subscribers will get the same random number now in my experience I'm not usually taking cold observables and making them hot instead I'm creating subjects or behavior subjects you can think of a subject as a hot observable but it has the added benefit of being able to have new values pushed to it in plumbing terms it's more like a pump where you can add new values to the stream after it's been created we can do this easily by just instantiating a new subject and then we can subscribe to it just like we've done with our other observables what's unique about it is that it has a next method that we can call to add new values to the stream now a potential gotcha with a regular subject is that you need to have the subscription setup before you start adding values to it you can see here in this first example we subscribe then we add two new values to it and then we set up a second subscriber and what you intuitively might think is that both subscribers will get the same values but that's not true for the second subscriber which will get nothing because it's subscribed late after the values were already at and that brings me to one of the most useful things in rxjs which is the behavior subject it's similar to the subject we just looked at except it has the concept of a current value this means the last emitted value will be cached similar to how we set it up with share replay earlier but most importantly it means that every subscription will always receive a value even though our second subscriber came in late it still gets the last emitted value in the stream and that tends to be a very powerful feature when doing things like state management and front-end applications at this point we've only been talking about creating observables now it's time to look at operators which help you control the flow of data going through your observables there are a ton of operators built into rxjs so we'll just look at a handful of some of the most popular ones first we'll create a source observable the NIEM it's the integer is 1 through 10 you can compose multiple operators together by using a pipe and just like it sounds your data will flow through this pipe and then be modified by each function or operator in the pipe if we subscribe to the empty pipe it just emits the values of 1 through 10 to take the input and simply transform it to a new output you can use the map operator for example we can take each number and then raise it to the power of 2 now the observable emits the square of the initial numbers another thing we can do is accumulate values as they flow through the observable similar to a rate reduce if we want to keep a running total of all the values that were emitted in this observable we can use the scan operator and then add the current value to the accumulated value now each emitted value gets added on top of the previous one an important thing to keep in mind here is that the order of operation matters for example if we move the scan operator before the map operator we're going to get different results because the underlying math is now different so that's how you transform values but another useful operator is filter which prevents certain items from being emitted in the stream for example if we only want values that are greater than 10 we can use the filter operator and that will only emit values that meet this condition and now we only see values that are greater than 10 printed in the UI another thing we can do with operators is tell the observable to complete for example if we pipe in take 3 it will only emit three values from this observable then complete it the next thing I want to show you is an operator called tap which allows you to trigger side effects from inside the observable pipe in all the examples so far I've been printing values to the screen by using the callback in the SUBSCRIBE method but that only gives us access to the value at the end of the pipe the most simple use case that you'll find is just console logging at different points within the observable pipe the pipe modifies things from top to bottom so if we add a tap to the very beginning it's going to print the initial value and if we map it to something else and add another tap after that it's going to print a different value and you can use tap to trigger more complex side effects for example you might want to save a value from the observable to a back-end database if you're using something like firebase you'll probably wanna use an async function so basically it just gives you a context to tap into the observable and do something another term you might hear with rxjs is back pressure and basically that means you have an observable that's emitting way more values than you actually need we can simulate this in the code by listening to the mousemove event on the Dom moving the mouse across the screen will cause the observable to emit multiple events every second rxjs has several operators that can help us with this situation the first strategy we'll look at is to debounce the events ad bounce will filter out all events until they have stopped happening for a certain period of time in this case 8,000 milliseconds ad bounce is really useful for something like a type-ahead where you don't want to make an API call until the user is done typing you can see here if we mouse around for a little while nothing happens then if we stop our event will be emitted after one second an alternative to ad balance is a throttle it will emit the first value but make sure that no additional values can be emitted until a certain time period has passed if we go back to the demo and start moving around at the very most we'll get one event emitted per second these operators are alleviating the back pressure but they're also filtering out a lot of data if we want to keep all the data but just not listen to it all at once we can use a buffer this will collect all the events into an array and then only emit them when they get to a length of 20 now we're going to switch gears to another very important operator called switch map this allows you to start with one observable and then switch to another one which is very important when we're talking about relational data for example if you're a firebase user you probably have an observable of a user logged into your app and then you might want to switch to another observable of some information about that user in the database we can simulate that in our code by creating an observable of an object and then I'll create a function that returns an observable of some information based on a user ID we can't call this function until we have a user ID so the question becomes how do we get the user ID out of the observable the naive way to do this is to nest subscriptions within each other so first we subscribe to the user that will give us the user ID then we create another subscription inside of that call for the orders that code will actually work but there's a better way to do this instead we'll start with our user observable to compose a new observable called orders we first add a pipe to the user and then we use the switch map operator which will give us access to the user ID and then we just returned an observable from it and you can also return a promise or an array here as well so if you ever have one value that depends on another value switch map might be the operator that you're looking for but there might be other cases where you have multiple observables that you just want to combine into a single stream we'll look at two different functions here called combine latest and merge and keep in mind these are not technically operators so we import them directly from rxjs I'm creating a cold observable of a random number and then I'm creating a second observable that just adds a delay to that random number first we'll use combined latest and it takes an array of observables and it will wait for each observable - you made a value and then he met everything together as an array even though the random numbers are going to emit something right away it's going to wait for that delay to resolve into a value before it emits anything then if any of the observables emit anything after that it will emit everything again as an array in other words it gives you the current state of every observable in this array so initially it's blank for one second waiting for that delay and then it emits out the four random numbers in many cases though you don't want to wait for that delay so instead you can use merge which will just emit each value one by one as it comes in through the stream merge doesn't care about the array position of the observable it only cares about when it emits it in the context of time so it emits the first three values and in the delayed value a second later another thing you might be wondering is how to catch errors in the context of a stream you have a lot of flexibility here but one of the most common strategies is to catch the error and then replace it with some other value we can do this easily with the catch error operator it can intercept the error and then you met some other default value out of the stream and one thing that's really nice about rxjs is you can easily retry things by just piping and the retry operator I'm not going to get into it here but it's super useful when working with HTTP calls first of all push a regular value and then an error to the subject if we don't catch the error we'll get this uncaught error in the console but by using the catch error operator we can handle it in the background and then provide some useful information to the user and the front end now if you're a good plumber then you shouldn't have pipes that leak and by that I'm talking about memory weeks there are two main ways to prevent memory leaks with rxjs the first way is to unsubscribe from your subscriptions take for example this interval this interval is going to run forever so if we don't close the subscription it's just going to create a memory leak in the background one potential way to handle this is to create a variable for the subscription and then call subscription unsubscribe at some other point in the code this will stop the leak because we can see here the observable only emits 10 values but there's actually a better way we can do this we can pipe in the take while operator to the interval itself and that will tell it to emit values only while a certain condition is true when that condition becomes false it will stop your minion values therefore preventing the memory leak and we don't need to manually unsubscribe anywhere in our code that works well in a lot of situations but sometimes you want some other observable or subject to be the thing that triggers the subscription to stop for that we have the take and tell operator and it takes an observable as its argument and when that observable emits something it will cancel the subscription on the source for example if we wanted to set a timer for two seconds on this interval we could do that by using take and tell with a timer observable when the timer finishes it will complete the interval and then end the subscription I'm gonna go ahead and wrap things up there we barely just scratch the surface with rxjs so let me know what you want to see next in the comments if this video helped you please like and subscribe thanks for watching and I will talk to you soon [Music]
Info
Channel: Fireship
Views: 176,576
Rating: undefined out of 5
Keywords: firebase, webdev, app development, typescript, javascript, lesson, tutorial, rxjs, reactivex, streams, reactive programming, rx, react, vue, lodash, angular, js, fp, functional programming, functional js, functional javascript, mario, rxjs tutorial, rxjs crash course, webpack
Id: ewcoEYS85Co
Channel Id: undefined
Length: 14min 43sec (883 seconds)
Published: Mon Mar 25 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.