• Meteor

The Computation Object

From the class:  Tracker

To start us off to dig into the internals of how this works-- in other words, how we're able to rerun a function-- we're going to look at a special data structure. It's one of the core data structures in the tracker package called computation. So over here on the left, I have my app code, and it's just going to be an app.js file, and [INAUDIBLE] tracker only runs in the client, so that's why we have this if statement here.

And in the right, I've taken the debugger out and just made it the entire screen, because we're not going to be working with user interface for the rest of this class. So the first thing we need to do is to look at how we create a computation and what it allows us to do. So computations are really about functions and rerunning that function. So let's start off by creating a function.

I'm just going to call it sayHello, and we're going to assign it to a function, and I'm going to name it so that we can see it in the stack. And all I want this function to do is to just print out Hello World and to make sure that it works, when I come over to the console, I can just call it like this. And it says it's not defined. Let's find out why. Looks like hot code push didn't quite keep up with the speed of the class here.

So when I call the function, it prints out Hello World. And of course, if I want to call the function again, I can call it as many times as I want. But what I need to be able to do is to re-run this function when a value changes. And so we need to build up the machinery to be able to do that.

So to create a computation, which is the core object that we're going to be using in the Meteor reactivity system, we call the Auto Run function of tracker. And what Auto Run takes as a parameter is a function. So notice we're not calling the function here. We're just passing it as a value. And what this will return is something called a computation. It's the actual computation object.

Now, I've assigned this to a global variable so that we can take a look at it in the console, and let me just type computation, and we can inspect this object. The first thing to notice about this is that a computation is a regular JavaScript object. So if you've taken the JavaScript objects class, you should have a good intuitive sense of what a JavaScript object is, and that's exactly what a computation is. It's a JavaScript object that has properties and values.

And the first key property to look at is that it has this underscore function property, and that's assigned to the function that we just created over here. So we would say that the computation wraps our function, and it wraps it with some capability. I'll show you that in a second.

Now, if we look at some of these other properties, you'll see some state variables or state properties. For example, has the computation been run? So is it the first run, is it invalidated, or is it stopped? And if you expand into the prototype of this object, you can see some of the functions that we're able to call on the computation. The one that we're going to spend the most time on is invalidate and stop, and there's a couple of private functions that Meteor uses internally.

So let's play around with some of the functions that are associated with the computation. So to do that, I'm going to come over here. And the first thing I want to do is call the invalidate function. And notice what's happening here is it's just rerunning the function, and I can call this invalidate method as many times as I want, and all it does is rerun the sayHello function.

If I call the stop function and then call invalidate again, notice that this time the function doesn't rerun. So we say that the computation has been stopped. If we look at the computation object now and drill into its properties, you can see that the stopped value, the stopped property, is now true, and that's because we've stopped the computation from running.

Now, so far we've been working with the computation as a variable that gets returned, the value gets returned, from calling the Auto Run function of tracker. So this Auto Run function is called a constructor because it constructs this object that we've been playing with, called this computation. There's another way to get access to the computation, and that is that it gets passed in as a parameter to the function that's passed into Auto Run function.

So that'll get passed right in here. If we wanted to use it inside of our sayHello function, we could do that by referencing the first parameter here. So I'll just call it C for short for Computation.

Now, one of the useful functions of computation is that we can pass in a callback that will get called any time the computation is invalidated but before the function gets re-run. And so let's call the OnInvalidate method of the computation. And this time, I'll just pass in an anonymous function. And the only thing this is going to do is to just print out the computation so we can play with it in the console, and I'll pause us in a debugger so that we can see this while it's transforming.

Now in the debugger, notice that the computation runs our function once the first time. And so as soon as we pass in the sayHello function to Auto Run, it runs once, and that's why we're seeing Hello World printed to the console. And now what I'm going to do is invalidate.

Let me clear this up. And I'm going to call the invalidate function, and that's going to drop me in the debugger, and I can pull up the console here, and we'll take a look at the object.

Now, notice that the state of the computation has changed. In other words, there's a property called invalidated and that's true. The stopped property is still false because we haven't actually stopped the computation and the first run property is also false because it's already run once. You can see that by looking at what the function has run here.

So we're currently invalidating at this point, but the function has not been rerun yet. So notice that the recomputing property is false because the function that is going to re-run is not rerun yet. So if we press Play here, once the function reruns, we see that show up in the console. It looks a little bit hidden, so let me scroll down.

Looks like we got an error about the WebSocket because we waited too long. But you do see the Hello World printed here to the console. And so the first thing that happens before the function gets re-run is this OnInvalidate callback will get called. So that gives us a chance to do something, some cleanup or other types of things, before the computation re-runs.