Transcript from the "MergeAll & SwitchLatest" Lesson
>> Jafar: We've already talked about concatAll, and the way it works top to bottom left to right, just the same way that worked with the right. So, if you're ever confused about how observable works with concatAll, just think about how an array get flattened with concatAll all. There's two more flattening strategies that I wanna call your attention to.
[00:00:17] And together, these three flattening strategies, you're are gonna be able to solve most of these same problems with. The next flattening strategy I want to call attention to is what happens when you've got three lanes of traffic, and one gets closed down because of an accident, at least in a civil society, what happens?
>> Speaker 2: They merge.
>> Jafar: They merge, first come first serve, right? If whoever gets there first merges in, that would be the civil way to do things. It might not actually work out that way in practice, but in a perfect world, right? So, let's run this and see what happens when we merge four lanes of observable, so to speak, into one.
[00:00:51] So, once again, we subscribe we for each of the outer observable, and so, we start getting inner observables arriving over time. Once we get an inner observable, we forge each other that, and one comes out immediately, and this time, you're immediately returned, just like in concatAll. Now, a second observable, that one finishes, a second observer comes along, we forge over that, we return two as soon as it comes out.
[00:01:11] And now we're waiting for three. Now, in the previous example, concatAll would not emit any more values from subsequent observables until the observable is currently listening to completed. Not so with merge. So, while we're listening for three to come along, another observable comes along. This time it's empty, we forEach over that, what happens to an empty observable when we flatten it?
[00:01:33] It disappears. None of it, there's no data in there, nothing comes out. We're still listening for the second observable to complete, right? Another one comes along, merge immediately before it reaches over this one. It doesn't buffer it. It immediately forEach over observables, as soon as they arrive, and we get out four.
[00:01:49] And then that completes, and then once that completes, we're still listening all this time, we've been listening that second observable. Finally, it gives us a value and then completes and we get. This order right here. So in concatAll, data is emitted based on the order of the collection that it was inside of arrived.
[00:02:09] In mergeAll, doesn't matter, it's first come first serve. Whatever data comes out, we immediately emit. We don't buffer up observables we just forEach over them as soon as they arrive. And as soon as their data comes through, it just gets emitted. Is that relatively straightforward? So, that's why we got items out of order here.
[00:02:22] Because we, you use mergeAll, when you don't care about order, and you just want data throughput. You want data throughput as fast you can, you don't care about order. Use concatAll when you care about order. Does that makes sense? We'll come up with more concrete examples later. The final pattern in this is perhaps the most common flattening pattern you will use in user interfaces, you use it all the time is switch latest.
[00:02:43] So, switchLatest starts out just like concatAll, so we subscribe the outer observable, we get an inner observable, we subscribe to that, we forEach over that, and then we immediately emit a value, then we begin. Then we get a second observable, we forEach other that. Two pops out, we immediately emit that, but while we're waiting for that, second observable to complete.
[00:03:05] Another observable arrives, and what switchLatest does is it switches to the latest observable. It says to the second observable, I don't want any more data. By calling subscription.dispose, so it unsubscribes from that previous observable. So, we're never going to get the value three. Cuz we've said to the producer, I don't want any more data.
>> Jafar: And so, once again, empty collection gets flat. And there's nothing inside. And then another observable comes along. We return this. And finally, when the outer observable completes, when the outer observable says, I've got no more inner observables for you, the whole thing completes.
>> Speaker 2: With the empty one, do you want to kill the second one, or only with something with data will kill the second one.
>> Jafar: Really? That's a really good question. Another way of doing switchLatest, and this is actually a legitimate plotting pattern you could use, which is called switchLatest on notification. Another way of handling it would be, instead of switching from the second one when the third one arrive, would be to wait for the third one to give us a piece of data.
[00:04:06] And that's what you're kind of saying, right? What if we waited until the third one gave us a piece of data before switching? That's not how it works. We switch as soon as we get another observable. We don't wait for the observable to give us data.
>> Speaker 2: So, the observable will kill it?
>> Jafar: Will kill it. Now, that's actually totally legitimate way of doing things as well. There should be a pattern for that as well. I think there may be in RX, there may not be. Frankly, this turns out to be the most most common thing. Now, why is this the most common thing?
[00:04:30] Because in user interfaces, users do this all the time. Let's say I've got a simple button on a form, and every time you go out and click it we go out and check Netflix's stock price, for example. Put it on screen, right? Now, let's say I give that to my user, right?
[00:04:43] What's the first thing that that user, or better yet, let's say I give that to my QA person. I've got a quality assurance person at Netflix. Yep, the first thing that that quality assurance person is gonna just bang on that button as fast as they can. They're such jerks.
[00:04:56] We're always trying to break stuff. What's gonna happen if they jam on that button? Does anybody have an idea?
>> Speaker 3: It will just take the last one?
>> Jafar: Well, with switchLatest, absolutely right. It'll cancel any outgoing requests. Maybe before they were made. Because remember, browser has queues of requests that are outgoing, right?
[00:05:13] And it will cancel a pending request. You might actually be able to cancel a request before it even heads out, right? So, it turns out to be very efficient to use switchLatest because you only got so many sockets that you can open to a server. There is only so many concurrent connections you can have, and that's why observable is so useful because the consumer can say to the producer, I don't want any more data.
[00:05:33] That gives the producer the opportunity to cancel or prevent any work that had scheduled to happen from happening. So, in this case, the browser has a queue of outgoing requests, and if it turns around and calls abort right at the producer, the observer becomes around and returns and says abort on the outgoing request, it might actually prevent a request from going out before it even is issued to get it.
[00:05:53] Sorry, you got a question back there?
>> Speaker 4: Yeah, how does switchLatest know when to dispose of some sort of debunks going on?
>> Jafar: Well, it knows when to dispose because remember, switchLatest is subscribed to an outer observable. And every single time it gets an inner observable, if it already is listening to another inner observable, that's what it knows to dispose.
[00:06:12] Because its job is to only listen to one inner observable at a time. And so, as soon as it gets a new one, if it's listening to another one, it disposes of that one. So, it disposes of the inner subscription to the inner observables it's currently listening to as soon as a new inn observable comes along, does that makes sense?
>> Speaker 5: So, in your button example, it's only going to listen to the first click, and then all of the clicks will be ignored, or it will be the last click, or how does that work?
>> Jafar: Good question. SwitchLatest. So, we what we're really doing is we're taking clicks, and when we see this example later on, makes more sense.
[00:06:45] But what I'm actually doing is I'm taking clicks, and I'm mapping over every click, and I'm replacing it with an observable. That is actually the result of a network request. And so I'm taking a flat observable of just quick object quick object quick object click event object right And I mapping it into a two dimensional observable because for every click object I am gonna substitute in an observable that represents the result of the network request.
[00:07:09] And so, now I have a two dimensional observable, and what switchLatest is gonna do is it's gonna say, well, whenever a new observable comes along, because I've created this two dimensional observable with map, when I apply switchLatest to it, now I have two dimensional observable. And undo inner observables only gonna come along when another click happens.
[00:07:24] And so, switchLatest, it doesn't know about the clicks or the map operation, all it knows about is two dimensional observable. And it knows that whenever a new observable comes along, it disposes of the old one and starts listening to the new one. And just by calling dispose, what it's calling dispose on is on that observable that represents that asynchronous operation.
[00:07:41] And so, when it calls dispose on the subscription object, returned by that observable that kicks off the asynchronous operation, that producer can just say abort the XHR, abort the network request that goes out. But I know that's a lot, that's a mouthful. We'll have examples and we'll go into it more clearly, right?
[00:07:57] But that's effectively what's going on. Every single click gets mapped into a network request. Which is also represented as an observable. And so, we get a two dimensional observable, and we're only interested in one network request, one request for the stock price, at a time.
>> Speaker 5: Which one's the first click here?
[00:08:12] Is it one?
>> Jafar: Every single one of these observables represent a click because
>> Speaker 5: But the first one, that would be the one, right?
>> Jafar: One, yes. Every single one of these observables represent a click because every single time I click a button, I've mapped that into a network request.
[00:08:23] So, just think about each of these observables like a stock price. I know it's, there's multiple values in here, so you expect to see an observable of one, and you'd expect to see a bunch of dots in front of it. That's how you would. That's if you were to visualize what a network request would look like?
[00:08:36] With every thought network recently returned one value rule, it would look like dot, dot, dot 52. And then it ends, and that's what a network request would look like it's an observable. Yep?
>> Speaker 2: I'm generally sad about losing data, are we sad that we lose our click stream?
[00:08:48] We don't necessarily-
>> Jafar: We only lose the clicks, notice that this stream, if the source of this stream is clicks, this observable will go on forever. The flattened observable will go on forever because it could always be another click which always creates another network requests. So, the only thing we're losing is the the stock price that we tried to get for an individual click.
[00:09:12] And that's a nuance. Sometimes it's hard for people. They think, we're, we stop this and give the outer observable. No, we stop this and give the inner observables. And this, and we've stopped listening to it presumably because there's something newer that we're more interested in. And this happens with you guys all all the time, right?
[00:09:26] I jump into a form, and an animation starts, but then I hit back. I want to cancel that animation, or stop that animation, right? It happens all the time in the users interface, which is why it's gonna be most useful pattern that we're gonna be using in user interfaces.
[00:09:40] Now, if you didn't have switchLatest, you'd build state machines. You would have variables saying, I've got an animation going. I gotta remember that I have an animation going, just in case somebody gets back, is that I need to check whether I've got animation going. And then I need to cancel that animation and then it gets hard.
[00:09:54] It gets really hard. I'm gonna show you how to do all this stuff declaratively. Instead of building a machine with a bunch moving parts to compute an answer, we are gonna write the answer, and that's all the difference in the world. We're gonna do this declaratively. I got another question at the back.
>> Speaker 4: Yeah. They were talking about an example here for having a music player with a previous next song buttons, switchLatest, would that accidentally go to immediately next to previous song? Regardless of the amount of times you clicked the control?
>> Jafar: Well, it's hard to say without knowing how this two dimensional observer was created.
[00:10:29] I mean, every click could create an action object. Right? Whether you click back forward or next or whatever, you could map all the clicks on those buttons into actions, which is observables that are asynchronous. Because start doing player option actions may be asynchronous, right? When you play, it might need to go to the server to load it, but some of those actions might be synchronous.
[00:10:47] Stop might be able to be executed synchronously. I don't know, but let's imagine for a second that each one of those player options was asynchronous. Maybe we're talking with a native player object. So, we have to send a message and we get a call back, right? Maybe it's not even a, maybe it's a browser object that we need to talk to through a native asynchronous interface.
[00:11:03] So, let's say every single one of those clicks was asynchronous. We asynchronously got back a message telling us weather was successful or not. Well then, let's take each one of those clicks, map them into an observable that represents that specific player action. So, when I call play on the player, I get back into the observable that completes when that play is actually successfully occurred.
[00:11:21] Well, then we have another observable of observables, and then switchLatest would be perfectly fine, because if I hit skip forward to the next chapter, right? And then I decided, no, I want to pause. If the skipping forward took a certain amount of time, then we could cancel skipping forward, and then we could just switch over to the pause operation.
[00:11:38] Does that makes sense? So, this will be more concrete when we start to see the larger programs that use switchLatest, you'll be able to understand how this all fits together in context. For now though, it's just important we learn, focus in on this one thing, and understand switchLatest does to a two dimensional observable.
>> Speaker 2: I have a question, and so, this two, does that kill the three, or does four kill the three?
>> Jafar: Actually, the empty observable kills the three.
>> Jafar: So it's the third observable that comes along. And that's the distinction. I am glad you asked that question, cuz that's an important distinction.
[00:12:09] It's not when an observable gives you data that you stop listening to the current observable, it's not when a new observable comes along and then gives you data that you stop listening to the previous observable. It's when a new observable comes along. Data or no data, then you stop listening to the current observable.
>> Speaker 2: Okay, I understand.