Transcript from the "Exercise 31" Lesson
>> Jafar Husain: So in addition to being able to complete an observable with take, our very, very powerful function is takeUntil. You can complete an observable when another observable fires. Does that make sense? So I can say, listen to all the mouse moves until the next mouse up. So now, that's how I get away from writing all this event handler, add event listener, remove event listener code.
[00:00:26] I don't sort of say add event listener to mouse move, but then add event listener to mouse up. And then when the mouse up happens, unhook the event listener from mouse up, and unhook the event listener from mouse move. That's all very confusing. takeUntil does all of that stuff declaratively for us.
[00:00:40] So let's take a look at this. So let's go back to our NASDAQ example, one of the problems with the NASDAQ example that we ran earlier is that it will go on forever. [LAUGH] And it will eventually actually use up memory potentially, unless the browser's being really good about clearing that console buffer.
[00:00:55] It's just gonna keep writing to the console, right, and well, maybe we don't want that. Maybe we would like to just sort of see the stream for a while, and then decide, okay, I don't wanna see that stream anymore. Right, I just wanna stop it, right, you could certainly see that happening if I open a form, for example, in the UI, and I wanna see stock streams.
[00:01:11] But then what happens when I close that form? Obviously, I wanna stop listening to that stock stream, and maybe I've got a web socket under the hood that I also wanna close, right? It's these types of things that I want you have to think about when you're building UIs.
[00:01:41] Today we have a much more complicated model, right, those of us who are building angular apps or react apps. The notion of a page is really actually just very often another div popped up on screen. So you have a single page, and then you just keep loading forms.
[00:01:56] Well, that creates a lot of complexity, cuz now, you really gotta make sure to clean up after yourself. Let's say I open up a form, and then I start streaming in stock prices, right? Well, as soon as somebody hits X, I better close that web socket, right? So that I'm not continuing to access the NASDAQ, long after that person is actually looking at that form.
[00:02:13] So, this is the type of thing, this is why it's become so complicated, and asynchronous programming has become such an important thing for developers to worry about. As we build single page web apps where the page lives for a very long time, we wanna make sure not to leak memory because after a while, the applications slow down and eventually even crash the browser.
[00:02:30] So takeUntil is a really, really key tool to solve this problem, right? Imagine saying well, listen for form open, right, that's a stream too, right? When somebody clicks a button to open a form, I'm gonna listen for a form open, and then I'm going to begin listening to stock prices until the form is closed.
[00:02:49] That's a very common form that you're gonna see again and again. It's not so different than listen for mouse down, then I'm gonna be listening for mouse moves until the mouse up. You're gonna see a lot of that. Listen for the start condition, map it into some street listening to something else, right?
[00:03:05] Map it into some other observable until the stop condition. That make sense? So we're gonna start doing that map, that should be incredibly common in user interfaces. So here, I'm gonna use the exact same approach, except instead of listening to these stock prices forever, I'm gonna listen until, so we're gonna pass in a stop button.
[00:03:25] So I wanna actually be able to listen for the click for the stop button, it's right down here. So there's a stop button along with a run button, so when I click the stop button, I want the to stop printing out stock prices. So first thing I have to do is I need to convert the stop button into an observable.
>> Jafar Husain: So that's always the first step. Whenever you run across an asynchronous API that doesn't emit an observable, the first thing you do, convert it to emit an observable. Adapt it to the observable interface. Cuz now we can use all those cool functions like map and filter and so on and so forth, right?
[00:04:02] So now I just have to takeUntil(stopButtonClicks).
>> Jafar Husain: So now if I run this, we see our stocks. And you can think of this as the user going along and maybe closing a form, right? And they hit stop, that could just as easily be an X button on a form, and now we're not getting stock tips anymore.
[00:04:28] And under the hood, remember, when you tell an observable that you're not interested in listening to the result anymore, that gives the observable to do the opportunity to clean up after itself. So in this case, if I were really listening to the NASDAQ under the hood here, I could close that web socket and clean up after myself.
[00:04:43] And as soon as that observable says on completed, I'm not sending you any data anymore. Remember, the consumer here doesn't say, I'm not interested in the result anymore. What we did is declaratively force the observable into simply saying I'm done when this button clicks. And that way, we can be 100% sure that it's not holding onto our subscription anymore, right?
[00:05:00] Why would it hold on to a call back when it's never gonna call again? So this is how we make sure that when we build user interfaces with all these events that we're working up to, we never run out of memory, cuz we're always making sure to clean up after ourselves.
[00:05:12] Code like this is much easier to write than adding an event listener and removing an event listener because one thing I wanna call your attention to, remember I talked about changing things, right? Adding an event listener and removing an event listener is a classic example of changing an object you don't own, right?
[00:05:28] If there's a list somewhere that you didn't create of listeners, and now you're gonna push something into it, and then later you're gonna pull it out, right? That is happening under the hood, right? That's what's going on under the hood, but it's not something your code is doing directly.
[00:05:41] From the perspective of this, all your code is doing is it's taking a bunch of observables, combining them, and creating a new observable. That's all that's happening on this top level of code here. And I wanna call your attention to something that we talked about yesterday, but it's been a while since we talked about, all right.
[00:05:59] If I comment this out and then I run, we won't see any stock prices printed on screen. An observable doesn't do anything until you begin listening to it. All we've done is create a bunch of objects, which hold onto a bunch of objects, which hold on to a bunch of objects, hasn't done a thing.
[00:06:17] Think of an observable. It's a little bit like an event because it pushes you information over time, but it's also a little bit like a function, in the sense that nothing happens until you call it. Really, observable only has one function, which is forEach. All the other functions are actually implemented in terms of forEach, map, filter, reduce, and so on and so forth.
[00:06:34] They all use forEach under the hood. So that's really all an observable is, it's one object with a function just waiting to be called. So that's the way to think about it. It's a little bit hard to get your head around an observable, because it's a little bit like an event, and it's a little bit like a function.
[00:06:48] Got a question?
>> Off Camera 1: Question from chatroom, the priceNASDAQ, is that an observable?
>> Jafar Husain: Yes, so sorry if I didn't really hammer that point home, priceNASDAQ, I was sort of tricking you guys into thinking it was an array, just like every other example thus far. But I pass it an observable, and the point of doing that was to hammer home the equivalence of the API between the array and the observable.
[00:07:11] Everything you learned yesterday, painstakingly learned, can now be applied to asynchronous data sources, not just arrays in memory.