Check out a free preview of the full Asynchronous Programming in JavaScript (with Rx.js Observables) course

The "Completing the Close Button" Lesson is part of the full, Asynchronous Programming in JavaScript (with Rx.js Observables) course featured in this preview video. Here's what you'd learn in this lesson:

Jafar adds the correct functionality to the close button so it will hide the form, thus disposing the current stream of server request. He also talks for a few minutes about how to introduce side effects during the forEach cycle with the doAction function.


Transcript from the "Completing the Close Button" Lesson

>> [MUSIC]

>> J. Husain: Now here's where we get to a place where I want to talk about exceptions to rules, because every rule has an exception. And so one of the exceptions to rules here is that sometimes you want to trigger side effects, right, when somebody subscribes to an observable. Now in general that's going to happen, for example, when we make a network request.

So side effects happen sometimes when you subscribe to an observerable. Again, you want to delay them. You don't want to do the side effects when you create the observable, you only want to do the side effects when somebody forEach's over the observable. So you want to make sure that the observable is lazy.

Now let's take a look at the getJSON request we made, that actually, notice that that triggers side effects. As soon as you forEach over that observable, right, what happens? Well I'll just use the network request, and sure enough, that change is a global object. So it's not that you can't have side effects, it's that ideally you want to encapsulate them inside of the observable.

So that when somebody forEach's over that observable, then and only then do you trigger the side effects. So what would be nice is, and then instead in addition to doing this inside of the forEach, we can also use an operator called do action. Now what do action does is it makes sure that when you forEach over an observable, it injects some code that you can run to cause side effects to occur.

Does that makes sense? So if I forEach over an array, there's no side effects, right? They're not really changing any global objects. But with do action, I can cause a side effect to occur. So I was saying earlier, sometimes we wanna inject side effects inside of an observable when we forEach into it, right?

So we wanna cause a side effect to occur just by forEaching over an observable. So we already saw that happen with making a network request, right? That's what happens when you forEach over a network request or the observable that comes back from search Wikipedia. We end up triggering a side effect, somebody selecting all the text now, or at least on my screen.

Interesting, but it's not showing up there.
>> Speaker 2: [INAUDIBLE] Type in there.
>> J. Husain: You know what? They are selecting, it's just super faint on the projection. But someone selected.
>> J. Husain: Someone selected all the text. [LAUGH] Yeah. It's happening.
>> Speaker 3: The text is not clear.
>> J. Husain: Whoever out there is selecting all the text, do not select all the text.

[LAUGH] Pink is nice, but.
>> Speaker 3: Yeah, I guess. I guess we're gonna have to roll with pink.
>> J. Husain: This is the My Little Pony theme of text editing.
>> Speaker 3: Yeah, but the problem is there's more than one user in pink. So we can't pinpoint which one.
>> J. Husain: It seems to have unselected.

Thank you, whoever you are out there.
>> J. Husain: So we've learned about map, filter, merge all, zip, and reduce. We've learned about flattening patterns, so we've learned how to build a collection function, which is that you keep mapping until you have all the data you need in scope. You create your result, and then you figure out how many flattens you need, and where to apply those flattens, to get down to a nice flat collection.

And then use forEach to consume it. That's just the functional programming style of building collections. Now you learn that when we start working with observable we have an additional question to ask ourselves. Which is then that, in that last step when we do the flattening, the question is which flatten to use.

Do we use merge all, do we use concat all, or do we use switch latest, right? Now another thing about observables, that you don't have with arrays, is that when you forEach over an observable it may cause something to happen, right? It may actually cause a side effect to occur.

So that's the case with the observable that we get back from search Wikipedia, when we forEach other that. We actually trigger a network request that goes across the wire. So that can be something that we can use to our advantage in this particular case. So search button clicks.

Remember the problem we had, we said that which is that we were relying on the order in which those event handlers fired, right? Maybe this event handler fires first and then we display, or perhaps even create those DOM elements, and then later on this event handler process, but we can't be 100% certain of that.

So if we want to inject side effects, meaning when you forEach over an observable, something happens we can use the doAction function.
>> J. Husain: So the doAction function actually has the same API as forEach, the only difference is it returns an observable. So if you put doAction in here, this is technically the action to occur when you get an onNext, so you can take the value.

And in here we can do something. So in here we actually wanna take the side effect and put it right in here, so that whenever you onNext over SearchButtonClicks, it injects the side effect. Now what I have done here, cuz I've actually just created another observable. I haven't, not like forEach where we run something.

What we're doing is we're just saying when somebody calls forEach on this observable, do this action. So this is gonna return yet another observerable, which I'm just gonna alias to the exact same one as before. Well, actually it's better practice to sit. Let's let's say, it's always better to be semantic when you're naming things.

So we've gone from SearchButtonClicks to SearchFormOpened, or SearchFormOpens. I usually name my observables plural because there are streams of things for the same reason you wouldn't name arrays plural because they're collections of things. So I'm gonna call this searchFormOpens. And now I now, when we onNext searchFormOpens, that form has already been displayed.

Does that make sense? Because, this is the code that gets injected in when you call forEach. So now we can be sure that by the time this code runs, this code has already run. Cuz when we forEach over this, the first thing that happens is that this occurs.

>> J. Husain: So let's just confirm that this still works.
>> J. Husain: Hopefully, no because I'm not listing the searchFormOpens here, I'm listing to searchButtonClicks. So now that I've switched it to listen to searchFormOpens, We should see that it works.
>> J. Husain: Great. And if I type in ab, we get my search results.

Great. Now we wanna do the same thing with close clicks. Right? When you click close, we actually wanna hide this form. And so this is a lot of us to just inject some side effects into the close clicks observable such that when somebody clicks close we can be sure that we're gonna hide that form before we get any feedback when you for Each over that.

So here inside of close clicks we created close clicks make this smaller, and we're going to create a search form closes and that's going to take close clicks and add a do action. That just means we're going to do something when for each is called, and in this particular case you're just going to take this and hide it again [SOUND] And so now instead of taking until close clicks.

We're going to do a take until search form closes. Yeah question?
>> Speaker 3: It's asking what [INAUDIBLE] [SOUND] is asynchronous?
>> J. Husain: If the side effect is asynchronous, you can't do with do action. Do action is only synchronous, but what he's really saying, I think in effect, is what if you want to perform that side effect after an asynchronous operation occurs, and then what you would really need to do is you need to use map because you want to map And then create an observable that causes the asynchronous thing to occur and then do a do action on the inner observable, right.

What he's saying is I want to do a asynchronous action when item arrives through the stream. So then you would just map over just like we cause an asynchronous request to occur when we type a key, we will take an observable and win it on nexus, well out a map and And then we'll create an observable to represent that asynchronous action.

And then on that inner observable, that represents the asynchronous action, that's where we add do action and then at that point we know that the asynchronous action is completed and so that we can now synchronously cause of side effect to occur. Almost every side effect in a browser is a synchronous API.

Because when you work with a dom. It's all sync APIs for changing the dom, right? Remove child, append child. So if that's what you want to do. If you want to wait until some asynchronous event occurs. And then cause a side effect to do it. You would just map, return that observable.

And then a do action on that inner observable. Does that make sense? So now we make sure that everything executes in the correct order, anytime we want we can inject side effects into an observable, right? Because remember that observable is just a forEach method, and it's just like tacking on some code inside of that forEach path which you want to run at the top of the forEach method.

Think of that as the mental model for what doAction does. And doAction also has overloads, by the way. If you wanna do a side effect on onError, or onComplete, you just pass it, it's got the exact same interface as forEach. So you can just do something when an error occurs, or do something when a completion occurs.

In this case, we don't need to do that. Does that makes sense? So it has the same interfaces for each. So you can just cause some side effect to occur if an error happens to come through the observable or when a completion happens to go through the observable.

So that wasn't clear, I'll put that up one more time. Just the same interfaces forEach. So doAction, it's exactly the same as forEach, except forEach actually triggers the observable to do something. Whereas doAction just returns another observable that when forEached, will cause those actions to occur. So one of these is a lazy, doAction is lazy, right?

We're not we're not doing anything yet. We're just creating an observable that when you forEach over it, something will happen. And forEach is eager. By the time you call forEach, you're actually now doing something. Does that make sense? So any time you want a UI action to occur, whenever somebody forEaches over a stream or an event occurs, right, it's usually best to stick that in a doAction.

And just some side effect that's occurring alongside. So you see here that if we run this it should work.
>> J. Husain: Great. Now how do I know that if I close this, it's gone. But how do I know it's actually unsubscribed from the event handler? Let's try something out.

Let's try not hiding it onClose, let's comment out this close here. And let's run it again. And then so now we know it won't be hidden. But if everything worked correctly, if I type into the text box after clicking Close, I shouldn't see anymore search results go out.

Because we've stopped listening to the inner stream of search results sets the moment we were only within that stream until the close button is clicked. So now that I've clicked Close, right, hopefully. Nothing. So that's how you clean up after yourself when you close and open sections of the user interface.

You always think about in terms of there's some of that which causes this form to open, and there's some of that which causes this form to close. And so just like sequencing any action with observable, you take the thing you want to happen first, you map, and you return the thing you want to have second.

And then that gives you a two-dimensional observable because for every item in the stream that you want to happen first, you're substituting in an observable for each one of those items. Then you have to pick the right flattening strategy, right? That's the question you have to ask yourself when you work with observable, but not with array.

With array it's just how deep am I? Well then I know to apply that may concatAlls. But with observable, it's how deep am I, right? And which flattening strategies do I apply at each step to get the right concurrency pattern that I want? So now we've explained merge all, switch latest, and concatAll by visualizing them to hopefully try and capture of that mental picture and project that mental pictures as you're going through.

And solving this problem to trying to decide behavior you want. Because there are cases in the UI for all of these things. You'll find cases for merge all, you'll find cases for ConcatAll, but for the most part, you'll find cases for switch latest. You'll only want to do the latest thing.

So I'm gonna uncomment closeClicks, and now we've built something that's very much akin to the type of thing that you're gonna build in user interfaces. This is how you will build your user interfaces. Someone will open a form, you'll have some asynchronous action occurring while that form is open, and then if somebody closes that form, you'll want to completely clean up after yourself.

And this is exactly the pattern that you use. I'm gonna fork this so people can come back and refer to it. So search. No search results, that no surprise.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now