Transcript from the "observables.shift() and Recursion" Lesson
>> Jafar Husain: Who's confused? Who's like way out there, like, what the hell? This is crazy, I have to initially call this function, so I'm basically kicking off this function now.
>> Speaker 2: So observables.shift, remind me. So arrays a mutable so when you do a shift, it actually mutates the original array?
>> Jafar Husain: Yeah.
>> Speaker 2: Okay.
>> Jafar Husain: But is that a good thing here?
>> Speaker 2: Yes.
>> Jafar Husain: Do I want to do that?
>> Jafar Husain: I'm not so sure, right. Let's think about how CONCAT works. If I call CONCAT 50 times, how many observables am I gonna get?
>> Jafar Husain: 50, right?
[00:00:37] Cuz it's a factory function. It creates new observables. But how many copies of this observables array am I gonna get? How many?
>> Speaker 2: One.
>> Jafar Husain: No, I'm actually gonna get 50, because every time the function's called, we create this new observables array. But let's say I call CONCAT once, and I subscribe to this observable 50 times.
[00:00:57] How many copies of the observable array am I working with here?
>> Jafar Husain: It's cuz of the way closures work. How many copies do you think I have? So I call CONCAT once, create one observable, and then I call subscribe 50 times. How many copies of this do I have?
>> Speaker 2: One.
>> Jafar Husain: It's a tough one, but yeah, just one. So It seems like shifting here is not a good idea. Because I am actually gonna be mutating just one copy. And then every single time someone else comes along and subscribes well, they will have one less observed bowl.
[00:01:34] So how do I avoid this problem? Anybody got an idea?
>> Speaker 2: We do like for the use head in rest sort of, type of recursion?
>> Jafar Husain: If what you're suggesting is that we could turn this into an immutable length list, and then go over it, that might work, more complicated than what I had in mind.
[00:01:56] If I have an array, and every single time somebody subscribes, I want to just have my own copy of that array, how can I make sure that works? How can I get my own-
>> Speaker 2: Do a deep copy?
>> Jafar Husain: Yeah, do a deep copy, right? Or in this case, even a shallow copy cuz I'm not actually mutating the observables inside.
[00:02:13] So I could just go with a shallow copy. So I'm gonna go let myObservables = Observables, that's slice and slice is not only it's just gonna give me a shallow copy of all of the observables inside. I wanna take a beat. Why don't I do a deep copy?
>> Jafar Husain: Can I change an observable? If I call subscribe on an observable do I change it? Let's take a look at the definition of observable. It's pretty much all right here. Now when you call subscribe on an observable, do you change it? It's just object. Like a objecty wrapper over a function.
[00:02:53] That's all it is. So I don't need to worry about cloning the observables. They're immutable. They never change. So here we go. I'm gonna processObservable. So it seems like what's gonna happen now that I've made a copy, seems like we're in pretty good shape here.
>> Jafar Husain: All right, but what's missing?
[00:03:15] Thing about recursion is that you need a time to stop, right? Otherwise I'm gonna keep going and keep going and something bad's gonna happen. Can anybody think of what's gonna happens bad?
>> Speaker 2: Stack overflow, well, it's gonna have an exception when the array-
>> Jafar Husain: Yeah. I don't even think we're gonna get to experience the joy of a stack overflow-
>> Speaker 2: Right. [LAUGH]
>> Jafar Husain: Because we're gonna try and pull off something off an empty array, right? So how do we guard against.
>> Speaker 2: Firstly, I like the head and the rest approach, and then you check to see if the rest is well, I suppose you can still do the same thing with the mutable of observables.
[00:03:49] Just check if it's empty or not.
[00:04:04] And there's a really cool linked list data structure that's basically a recursive data structure in itself, in the sense that a link list can either be a value, or nothing that is, or a pair of a value and another linked list. That's a whole other topic or whole other tutorial.
[00:04:19] For now, let's just check that this thing isn't empty before we go on, right? So before we execute this code right here, we wanna check, hey.
>> Jafar Husain: I think I moved this out here, I wanna put it in here. Right, x, oops. So before we shift, we're gonna say, hey.
[00:04:43] If my observables length of zero. Now what are we gonna do?
>> Speaker 2: Complete?
>> Jafar Husain: Complete. Yeah, cuz we're done. We've processed all the observables.
>> Jafar Husain: Now this isn't quite good enough, because what's an observable supposed to return?
>> Jafar Husain: When you call subscribe on it?
>> Speaker 2: Subscribe, or-
>> Jafar Husain: Yeah, a subscription, absolutely.
[00:05:17] So that's a little tricky, I'm not sure how to create a subscription here.
>> Jafar Husain: Now the thing is, in here, internally I'm creating a subscription, all right. But it all happens inside of this function. How am I supposed to get a subscription out?
>> Speaker 2: You return the subscription inside the if, would it be?
[00:05:37] Which subscription the last subscription?
>> Jafar Husain: Yeah, that's the thing right I'm creating a whole bunch of subscriptions because I've got a whole bunch of observables. So that's hard, right? In the end I only need one subscription. So let me ask you this how many different observables does Comcast subscribe to at once, at one time?
>> Jafar Husain: One, yeah, if you give it five, it's just gonna go left to right, subscribing to one at a time. So what we could do is we could just create a variable,
>> Jafar Husain: A reference to hold onto whatever the current subscription is. And as soon as create a new subscription, we can just assign it to that variable.
[00:06:20] So notice here is our process observable function, right. But we have this closure, we've closed over this variable sub and now inside of here when we reassign sub to something, sub is always gonna point to the currently subscribed observable. Does that make sense?
>> Jafar Husain: Yeah, so can I return sub here, is that good enough?
[00:06:47] Let me make this just a little smaller so we can see it all at once. Is this good enough, can I just return sub, is that gonna work? What's sub gonna be the very first time? This thing runs. I forgot to call our little function here otherwise it's never gonna run.
>> Jafar Husain: So what's this gonna be? Well it depends, it's probably gonna be a value. Right it's probably gonna be something cuz we're gonna call processor observable, it's immediately gonna subscribe. And then I'm gonna return it, but a second later I'm gonna kinda replace sub to be the next subscription.
[00:07:22] But the subscription I gave to, where is he, to David, is still the old one, right? And now I've moved on to a next one. So if David unsubscribes from that, he's unsubscribing from the first observable, not the one that I'm looking at right now. So what I can do as I can return a subscription object that just takes now, because I've delayed this, I've just waited until you call on subscribe into function, I'll take whatever the current subscription is.
[00:07:53] And I'll unsubscribe from that.
>> Jafar Husain: That might actually work.
>> Jafar Husain: Does that make sense? So, we don't have time for another role play, but basically what's happening is I'm plucking off the first observable. I'm subscribing to it, right? And then I take the subscription that I get, I assign it to some reference that always keep track of the current subscription.
[00:08:18] In fact, I'm gonna rename that current subscription. So every single time I subscribe to a new one, I assign it to this reference current subscription. And then, when I get a complete message, I just recursively call it again. So I go up here. If I have another one, I pluck it off the front and I keep going.
[00:08:40] But if I don't, I just call observer.complete. I'm not 100% convinced this is bug-free. I'm pretty sure that, for example, I'm not doing everything I should be doing maybe. [SOUND] You know it might be alright with errors, other than the one little caveat about synchronous vs asynchronous observables we talked about before.
[00:08:58] This is a more complex example obviously. But that's basically what's happening. And if we wanted to, we could even step through this. Yeah?
>> Speaker 2: Within the error, there you need to change subscription to currentSub.
>> Jafar Husain: Thank you.
>> Speaker 2: Yeah.
>> Jafar Husain: I don't wanna subscribe from the currentSub, thank you.
[00:09:17] Right, and by unsubscribing from the currentSub, right, and not calling process observable again, means I'm not gonna move on to the next observable and subscribe to that. So that's how CONCAT works. That's the magic right there, now we know how it works.
>> Speaker 2: Will you repeat that last thing that you said?
[00:09:34] Like, so that was kinda like, I don't quite understand how you would unsubscribe in the middle, and then like how would that process look when you unsubscribe in the middle? And then it wouldn't end up subscribing to the next one.
>> Jafar Husain: Well, let's say that I unsubscribe to whatever.
[00:09:49] Let's say we're at the very first observable we subscribed to it. I get out a subscription and I assign it to currentSub, right? And now let's say this observable really takes its time. It's just gonna give you numbers over the next ten seconds, right? And you'll get antsy and say I don't need this I'm unsubscribing from this.
[00:10:05] Well, what's gonna happen? Well, I'm gonna unsubscribe from current sub, so how do we make sure that this isn't gonna move on to the next one, and pluck off the next observable and subscribe to that? Well, what happens when you unsubscribe is error or complete ever gonna get called when you unsubscribe from this observable?
>> Jafar Husain: If you unsubscribe from the observable, is it gonna call error, or complete, or next, or any method on your observer ever again? No, and so therefore, we never get to complete, we never call process observable, meaning we never start processing the next observable. Again, I'm not first time writing this, I'm not 100% sure it's bug free, but it looks like it's right.
[00:10:45] Does that make sense? So whenever you're working with asynchrony, the way in which we repeat things that are asynchronous, can we use loops?
>> Jafar Husain: Like the very first example I did, remember I looped over all the observables, and then I subscribed to them? Well, that would cause all the subscriptions to be observable to happen at the same time, but that's not what I want.
[00:11:10] In general, this is almost always true. When you wanna do things asynchronously, repetitively, when you wanna loop over something, but that's an asynchronous process. You can't really use loops, you have to rely on recursion or some function which indirectly does recursion. And so that's why we have made ourselves a little function and we just keep calling it.
[00:11:30] Does that make sense? So more advanced example than I necessarily wanted to get into but I basically wanna try and explain as much of the magic that we're gonna be doing as I can as we got through this process