Check out a free preview of the full Asynchronous Programming in JavaScript (with Rx.js Observables) course
The "Anatomy of an Observable" 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 reviews the anatomy of an Observable. An Observable is nothing more than an object with a forEach function. The observer referenced when creating the Observable will have onNext, onError, and onCompleted functions.
Transcript from the "Anatomy of an Observable" Lesson
[00:00:04]
>> Jafar Husain: Question?
>> Off Camera 1: Will observable fire when forEach is called no matter if they are a function that hasn't been called?
>> Jafar Husain: What's that?
>> Off Camera 1: Will observable fire when forEach is called even if they are in a function that hasn't been called yet?
>> Jafar Husain: You mean if forEach is itself nested inside a function and that hasn't been called yet?
[00:00:24]
>> Off Camera 1: I believe that is what they're getting at.
>> Jafar Husain: No. The point is the forEach function needs to run, right. And so if you haven't called this function yet, right, then you're not gonna get that forEach function run. Now if I call it, then in this case the forEach function runs.
[00:00:40]
So that's really just about whether the forEach function runs or not. And how we can create all these objects and have nothing happen is going to be pretty clear when we take a look at the implementation of observable. But I want you to get at least a 5,000 foot idea of what's really going on here.
[00:00:56]
Let's say I create a function called print and it's gonna console log hi, right?
>> Jafar Husain: I've created this function. If I run this now am I gonna see hi on screen? Yeah, right, now let's say I create a function to help me glue other functions together.
>> Jafar Husain: All it's gonna do is it's gonna take two functions and then run them one after the other.
[00:01:34]
And so I'm now gonna call then, I'm gonna pass in print, and then I'm gonna pass in another function which is gonna log
>> Jafar Husain: I want you to take a look at this.
>> Jafar Husain: Is any code gonna run? Am I gonna see hi on the screen? Am I gonna see hi and after on the screen now?
[00:02:06]
What's really going on here? You know what, I am, actually. Sorry.
>> Jafar Husain: There's a lot of functions going on here, right. But what I'm actually doing is I'm building a function and then I'm using another function to glue it together with another, with a function that I want to run afterwards.
[00:02:33]
But I'm still creating another function. Look at, with all the stuff, I haven't actually run any code. What I mean is, I haven't run the print code, nor have I run this code. Because if you look inside of what then's doing, it's just returning another function that calls the first function and the next function.
[00:02:51]
I know this is confusing code. But the point I'm trying to call out is that we're not really running the code inside of the functions. We're just creating bigger and bigger functions by gluing functions together. Does that make sense? Kinda? That's what's really going on with observable. Observable's just an object with a forEach function waiting to be called.
[00:03:11]
And when we call map, we glue it together with another function and create another function, with another object with a forEach function waiting to be called. That's what we call laziness. Instead of doing something, we create a function, which can be called later on to do something. It's a very lazy way of coding, right, and it turns out that in programming, laziness can be a good thing.
[00:03:31]
Can be a very, very good thing, cuz it buys us flexibility, right? A, one of the positives of this is I can actually run this function now as many times as I want, right? Instead of just running the function, I can create a function that I can call as many times as I want.
[00:03:45]
That's what an observable is. Remember when I said an observable that represents a network request will issue a network request again every single time you call forEach. That's really at a high level what we've been doing all along. Observable isn't so much of an object, it's really just window dressing around a single function, which is forEach.
[00:04:03]
And, we've been combining those forEach functions together every single time we call a map and a filter, and creating another function waiting to be called. So everything on the top half of this, it's just doing morally the equivalent of this. It's just creating functions. More and gluing more functions together to create new functions.
[00:04:23]
That's why nothing happens at the top of this code. Cuz we haven't really run any code inside of any of the functions. We've just been gluing a bigger and bigger function together waiting to be run. This turns out to be a really powerful programming technique. At a high level what we're really doing is we are passing functions to functions which create functions.
[00:04:43]
And that can be confusing but it's probably self-evident that that's actually a very high-level and powerful technique to build a program. You're at a very high level of coding now. When you're taking functions, gluing them together with other functions, to create more functions. And so that's one of the reasons why we're able to write so little code to do so much.
[00:05:02]
So that's the level of abstraction we're working out here. It's very expert level. But it doesn't have to be, we don't have to think of it as all the complexity. Because we can just look at these simple five functions, understand what they do to collections, and use them again and again.
[00:05:14]
But there's a lot going on under the hood. Yes. Question about what does observable use under the hood to run reactively? Well not much, in the sense that it's just thin window dressing over the simple asynchronous APIs that are already in the browser. So if I make an observable based on setTimeout, well when I call forEach, under the hood it's just gonna call setTimeout.
[00:05:36]
And then when I call subscription.dispose it's gonna call clearTimeout using that handle. Let's take a look, you know what, let's take a look at what an observable looks like. I wanted to get to this a little later but just so we don't feel like it's voodoo going under the hood here, let's take a look at how I would take set timeout, which is a browser API which fires after a few seconds.
[00:05:54]
And adapt it into an observable, cuz you can take any push API, any async API that calls you instead of you calling it, and you can make it into an observable. So in the browser you can call, setTimeout, and you can pass it a function, and then log something, and then you give it a time.
[00:06:14]
And after that time elapses, it will run your function. How many people have used this API before? A few people, yeah? So it's a useful little API, right? It also gives you a little handle, it gives you a handle, which is a way that you can use. Let's say after one second, you decide whoa, the guy closed the form, he doesn't want that to fire anymore.
[00:06:35]
Right, I don't want to show this alert box or do whatever I was doing, because now he's closed that form. He's hit the x button and he's gone away. Well, in the tradition of having many, many different async APIs, right? If you were working with a DOM event, you would call remove event listener.
[00:06:48]
But setTimeout is different, right. It's got actually another weird clearTimeout, I think it's clearTimeout, and then you have to pass in this handle, does that makes sense? I mean with the DOM even it's got a different interface, right, you call it create a handler,
>> Jafar Husain: and then a dom.addEventListener
[00:07:17]
>> Jafar Husain: And then if you wanna remove, it looks like this.
>> Jafar Husain: Notice both these things are kinda doing the same thing. I mean you're registering a callback to be called at a certain point, right. You don't necessarily know when it's gonna get called. And hen if you want to stop receiving any information, if you wanna cancel that, you call this API.
[00:07:38]
Here with the DOM event it's kind of the same thing, you're giving somebody else a callback, somebody's calling you instead of you calling them, right. And then later on if you decide you wanna cancel, it's a totally different API, but they're both doing the exact same thing. It's this kind of complexity that really doesn't need to exist.
[00:07:52]
We can have just one API for dealing with all push data sources, where somebody is pushing information to you, and that's what an observable is. So let's say I want to take this setTimeout API which is just an arbitrary design of how a push API should work, and I'm gonna convert it into an observable.
[00:08:08]
So it looks the same as all the other observable APIs. So I'm gonna use, now remember I told you an observable is nothing but an object with a forEach method, and I didn't lie. That's all it is, it's an object with a forEach method. And it accepts an observer object.
[00:08:21]
Now I want to take a little moment to remind you what an observer object. An observer object is a method, is an object with three functions.
>> Jafar Husain: That's all an observer is. Remember when you subscribe to an observable, it's a lot like subscribing to an event but giving three callbacks instead of one.
[00:09:04]
Does anybody remember why we have those two extra callbacks? What do they represent, what are they for?
>> Off Camera 2: Terms of completion.
>> Jafar Husain: Yeah. They're for completing observable contract with those two extra semantics that were left out of iterator all those years ago. We want the producer to be able to push a message to the consumer, not just be able to give them data, which is what onNext is for.
[00:09:27]
We also want a well defined way for the producer to say to the consumer, no more data is arriving, or an error occurred. So that's what those two callbacks are for. So when you forEach over an observable, like I did down here.
>> Jafar Husain: This is actually just a short hand.
[00:09:57]
>> Jafar Husain: This is actually just a shorthand for this.
>> Jafar Husain: So it's just a shorthand for only providing an onNext handler. That's all you're doing, you're creating, it's a shorthand, it creates the observer object for you. Does that makes sense? So whenever you implement an observable, you expect that forEach will accept an observer object.
[00:10:18]
Cuz RX, the underlying library, takes any possible overload of forEach and it basically converts all of those into observer objects. You only have to worry about, you don't have to worry about all the different possible overloads cuz I can also
>> Jafar Husain: We covered this in the presentation yesterday but it's been awhile so let's cover it again.
[00:10:44]
So, you can pass in onNext and then onError. You can pass in three callbacks.
>> Jafar Husain: Function with no arguments which is onCompleted. And that will just get converted into
>> Jafar Husain: An observer object.
>> Jafar Husain: Does that makes sense? So the first argument is onNext. The second argument onError. And the last argument onCompleted.
[00:11:22]
Or you can pass in an object with three named functions, onNext, onError, and onCompleted. It's totally up to you, either one of these will work. However when we're writing observables ourselves, we can count on the RX library of always converting into this form. So we'll always get an observer object with those three properties.
[00:11:42]
So under the hood RX takes this and converts it into this before passing the observer to an observable. Does that make sense? So it's got a little sugar where it just converts from one form to another. So when I write an observable, I can be sure that this observer object is in this form.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops