Transcript from the "Q&A: Subjects and Replays" Lesson
>> Jafar Husain: Any other questions? Subjects, that a great one. Yeah, so let's talk a little bit about subjects. A subject is a great example of how we turn observables into hot observables. So subjects for example are a hot observable, and what they do, this is actually a really elegant, beautiful abstraction.
[00:00:19] They take the two interfaces we have in observable, observable, and observer, and they smoosh them together into a new type called the subject. So the best way of teaching you guys about subjects is actually just to go back to our custom observable implementation and implement a subject. So a subject inherits from an observable.
[00:00:41] So let's make ourselves a subject.
>> Jafar Husain: A subject is basically a way in which you can broadcast a value to multiple listeners. So you remember we talked about why we might wanna set up an observable such that every new subscriber gets their own network request, for example, right?
[00:00:58] So but sometimes you want that behavior, like for retry, but sometimes you don't want that behavior. Sometimes you might wanna have five concurrent subscribers all just listen for the result of one network request. So how do we achieve that? Well, I'm gonna create a Subject. The Subject extends an Observable.
>> Jafar Husain: And all the subject does is it internally just keeps a list of all of its concurrent observers. And every single time you call next on the subject, cuz remember, a subject is both an observer and an observable. When you call next on the subject, it just loops through all of its observers and calls next on all the observers, does that make sense?
[00:01:40] So it's a mechanism for what we call multicasting, or broadcasting a message to multiple concurrent subscribers, without repeating the side effects, like showing a network request or starting a timer for every concurrent subscriber. So let's see how we can use a subject to take something like set timeout or a network request and set only one timer for a whole bunch of different users.
[00:02:03] Makes sense? So a subject looks like this.
>> Jafar Husain: constructor,
>> Jafar Husain: And super, the definition of a subscribe is quite simple actually.
>> Jafar Husain: This is a good opportunity to use map.
>> Jafar Husain: So when you subscribe to a subject, it just takes the observer, puts it into an array. That's all it does.
[00:03:04] And then,
>> Jafar Husain: I think this is delete. But as you guys are gonna see, I'm gonna set up a set, which is gonna be used to store all of the,
>> Jafar Husain: Does that makes sense to everybody? So all we've done is create an observable that whenever you subscribe up to it, it just says, thanks very much.
[00:03:29] I'm gonna take that observer, I'm gonna put it in a set, and I'm just gonna chill, okay? So as I said earlier, a subject is actually a combination of an observer and an observable. What are the three methods on an observer?
>> Jafar Husain: Anybody tell me, on an observer?
>> Student: Next, complete, error.
>> Jafar Husain: Next, complete, and error.
>> Jafar Husain: So when next is called, I'm literally just gonna,
>> Jafar Husain: Loop over all the observers and call next on in.
>> Jafar Husain: Anybody wanna take a guess what I'm gonna do if error's called?
>> Student2: Loop over all of them and call error?
>> Jafar Husain: Yup.
>> Jafar Husain: So what have we really accomplished here? I mean, we're just like a repository for a bunch of observers. And when everybody calls next, we just broadcast it to everybody. Why is that useful? So same thing with complete.
>> Jafar Husain: So it's actually very rare that you'll use a subject directly, however, the principal of broadcasting the results of an observable to many consumers is very important in our ex.
[00:04:45] And usually you will use operators that internally create subjects, like share and replay for example. So if I have an observable like the set timeout, the timeout observable we created earlier, where it's gonna trigger a new timer for every concurrent subscriber. I can use the share method to basically make sure that no matter how many concurrent subscribers, they're all gonna share the same timer.
[00:05:07] And under the hood, all share does is it creates a subject. It subscribes the subject to the observable cuz the subject is an observer. And that subscribes all new observers to the subject rather than the source observable. That's a lot of talking really quick, but we're gonna take a look at how that works.
>> Jafar Husain: And there's one little detail which [LAUGH] is important. If I don't do this, I open up the possibility that when I next an observer, they unsubscribe. [LAUGH] And if they unsubscribe, they're gonna remove themselves from the set while I'm iterating the set, and that's not a good thing.
[00:05:51] So I quickly make a copy of the observers, and then I make sure that I don't have that raised condition. So does everybody see my subject here? It wasn't that hard, right? We built the subject, pretty straightforward. All we're doing, socking away observers, broadcasting messages to them. So now let's see if we can write share, the share method.
[00:06:14] So that we can take our timeout observable and just instantiate one timer. Just start one timer for every single subscribed to the observable return from timeout. Now notice here, every single time somebody calls subscribe, we're gonna kick off a new set timeout method, right? Cuz it's right there inside of subscribe.
[00:06:32] How are we gonna rig it up so no matter how many subscribers, we only get one call to set timeout. This is a very important trick. It's used all the time because very often you don't wanna kick five of the same network request off at the same time, right?
[00:06:46] That doesn't make a lot of sense in a lot of cases. So I'm gonna write myself a function called share, and you can take any observable and you can share it. Under the hood, it's gonna create a subject.
>> Jafar Husain: And it's gonna go self.subscribe(subject).
>> Jafar Husain: Or wait, yes, and then it is going to, I forgot, sorry, I got to return a new observable, cuz that's what share returns.
[00:07:21] I do this right? [LAUGH]
>> Jafar Husain: This is not 100% right. But it's pretty darn close.
>> Jafar Husain: Does everybody see that? See what's going on there? I'm cheating. This is not exactly how share is implemented. But that's a basic concept and,
>> Jafar Husain: Let's go back and take our timeout observable.
>> Jafar Husain: Did I break that?
>> Jafar Husain: Good, just took a second.
>> Jafar Husain: Remember that timeout just on next undefined, that's why we see undefined there, all right? Now if I take,
>> Jafar Husain: Now, just to make sure this is working inside of our timeout observable, when we call set timeout, we'll log to the console.
>> Jafar Husain: Now I'm gonna take this,
>> Jafar Husain: Timeout Observable. I'm gonna assign it to a variable.
>> Jafar Husain: And then we're gonna call subscribe, but this time, we're gonna call it twice. And we expect that with each call to subscribe, we're going to get a new call the set timeout, right?
>> Jafar Husain: And sure enough, we do. What we're gonna try and to with the share operation is we're gonna make sure that only one call happens to set timeout. And again this will be less useful, actually it could be useful from the context of set timeout and more useful on the context of network requests, right, where I want one network request to be kicked off no matter how many people are listening.
[00:09:41] So let's see what happens when I go,
>> Jafar Husain: .share.
>> Jafar Husain: Well, it breaks. But we'll figure out why. Self.subscribe is not a function.
>> Student: 149, this equal self,
>> Student: 149.
>> Jafar Husain: Is it complaining about 149?
>> Student: There, enter your share.
>> Student: I think you want underscore subscribe.
>> Jafar Husain: I actually want this right here, cuz this is an instance method.
[00:10:18] So we call share on an observable, and so this, at this case, will be the observable. So let's give that a shot.
>> Jafar Husain: Uh-oh, right, it's not observers, but this.observers.
>> Student: 172 and 172.
>> Jafar Husain: 172, there we go.
>> Jafar Husain: And,
>> Jafar Husain: How about this new hotness?
>> Jafar Husain: There might be a better way of doing that. But that's what I'm gonna do for now.
>> Jafar Husain: There we go. Do we all understand what really happened there, right? We created one observable. It's here we got this link of chain. So it's observables calling each other, right? And we created one link in the chain that'll just store up multiple observers, and then instead of just going [SOUND] when we get to that step in the chain, it'll broadcast it to all the observers.
[00:11:55] And that's the key to how we make sure that if we've got an observable that we don't wanna be lazy, we can make it eager, in this case, and share the side effects across multiple consumers. Now, once again, I'm hand waving a little bit. The real definition of share is a little bit better than this, a little bit different than this, a little more complex, but that's the basic idea.
[00:12:19] So now you guys understand subjects. That's pretty much all there is to it. There are variations of subjects, like replay subject and behavior subject, and they're probably worth talking about, but the reality is, you're not gonna use it directly. In all likelihood, you're gonna use methods like share and replay.
[00:12:37] So share we just learned. Anybody wanna hazard to guesses for what replay does?
>> Student: Replay is what was previously nexted.
>> Jafar Husain: So one of the dangers around hot observables, which is what subjects are, is you might miss something that you wanted, right? Depending on when you subscribe to a hot observable, you might get a different result.
[00:13:00] And so sometimes what you want is you actually want, so let's say we create that observable that represents the network request and 15 people subscribe to it all at once. And then they all get the same network request, and we only issue one network request. But then two seconds later, somebody comes along and subscribes to that observable again.
[00:13:18] Well, we might wanna kick off yet another network request, or we might just wanna store up whatever the result of that network request is and just hand it to them. And that's what a replay observable is. It replays the observable for folks who come late to the party.
[00:13:32] Does that make sense? So after we've already, and that's all it is. And instead of share, it's a replay. And these are the two methods, and under the hood, they use different variations of subjects, a replay subject and a regular subject. But these are the methods that, I meant, these are how these methods work.
[00:13:48] Under the hood, they just use a subject. So now I'm glad somebody asked about that cuz we've come full circle. We've talked about the benefits of laziness, but we've also talked about how we can make something not lazy anymore in order to share side effects across multiple consumers.
>> Student: So in that replay example, where somebody comes late and you wanna give them that you don't replay it for everyone.
>> Jafar Husain: The idea would be, so let's say three people come and subscribe to an observable, right? You make one network request cuz it's a shared observable, and then you on next, and then on complete, all three of them.
[00:14:46] Or at least that's not gonna change very often, right? You can just cache it in memory, and then hand it to them. And that's all a replay subject does.
>> Student: It's like hanging onto a resolved promise and just giving them that.
>> Jafar Husain: Exactly, a promise, that's actually that's worth talking about.
[00:14:59] Cuz a promise is like an observerable that's always hot. It's always like a subject, and it always caches. So a replay subject basically turns an observable into something very similar to a promise, except it sends multiple values instead of one value. So promises have that behavior all the time.
[00:15:18] They are always hot and they always cache their results. So if you come after a promise is resolved and say, hey, give me the result, the promise will just immediately call you with whatever result it has cached.
>> Jafar Husain: Does that make sense? And so we can see that observables are more primitive than promises.
[00:15:37] You can build a promise by combining together methods on an observable until it behaves exactly like a promise, but you can't go visa versa. You can't build an observable on top of a promise, right? Cuz you can always go from lazy to eager. But you can't go from eager to lazy.
[00:15:52] Once you've started the work, you've started the work. It's a very flexible and powerful primitive.