Transcript
  • Meteor

Computations And The Call Stack

From the class:  Tracker

Next up, I want to show you how computations relate to something called the call stack. Now, this sounds a little bit complicated, but it's not really. It's just a couple things to show you in the debugger. And if you need any follow up with call stack and how the call stack works, you can go check out the JavaScript functions class, which explains it in more depth.

But the reason to understand this is it will make a lot of concepts a little bit more clear with computation. So the first thing to note is that when we call this function Auto Run and pass in say hello, Auto Run under the hood is going to actually call our function, just like we would do ourselves. And when you call functions in any programming language or in JavaScript, the call stack grows. So the function call gets pushed onto the call stack.

And this callstack will grow up. And then as the functions return, it will come back down. And there's only one call stack, so we don't have parallel call stacks or anything like that. We can only make function calls and return from those, and that's what we can do in JavaScript. So when we make a function call, the call stack grows.

Now, another thing to note is that normally, we don't call track or auto run unless we're running some or creating some custom reactive code. Most of the time, the framework calls auto run for us. For example, in our helpers, you don't see anything related to tracker.autorun, and that's because the Meteor core framework is doing that for you. Or if you're using a routing package, it might also be creating autoruns for you.

So inside of our functions, very rarely will we have access to the current computation as a parameter. So let's clean up this code a little bit. We don't need to deal with OnInvalidate for this segment, so I'm just going to go ahead and delete it. Oops. And let's pretend that we don't have access to this computation as a parameter.

Well, we can get access to the computation by using the current computation property of tracker. So I'm going to log that into the console and then drop us into the debugger and show you why this works. Let's expand this out.

And notice the call stack here. The call stack gives us some insight into how we ended up here, how we ended up at this debug statement. So there's some anonymous function calls, and then here's our tracker Auto Run function call. You see that right here, Auto Run? And then some other things happen, and ultimately, this compute function gets called, and then our function gets called.

And the point here is that there's only ever one computation that's active at a given time, and that computation gets set by this Auto Run call here. So when a computation is re-run, it also gets that. And so we can get access to the current computation with this property here.

And to make this a little bit more clear, let's look at another concept. You can nest Auto Run calls. So let's pretend that we have another one. I'll say tracker.autorun, and this time I'll pass in a function and I'll name it so we can see it in the stack trace there. And then inside of this one, I'm going to say let's name this up here C1 and this one C2.

And we'll come over to the right and let that refresh. So here we are in the first computation, and notice if we look at C1-- let's put a little watch statement here for C1-- it's a computation. And it even has an ID. So if you want to be able to identify it as a unique computation, you can see that here.

OK, next up, what we're going to do is go into the next computation. So I'm going to press play, and here we are in the second one. Now, I'll add another watch for C2 so we can take a look at it. And notice that on this computation, the idea is actually different. So this is a new computation that we created by calling the Auto Run function again. So this is the inner computation that's being run.

So because we only have one call stack, you can only have one active computation at a time. If we look at it from the call stack perspective-- so let me just close out this watch and take a look at the call stack. Notice that the first computation, C1, gets said here. So this is our first one.

And then we climb up the call stack. Here's us calling the say hello function, so that's this outer part here. And then here's the second Auto Run call. That's this one. And here is where the second computation gets created. And so right now, this is the active computation. And it's the one that this function has access to by calling current computation.

So you can only have one computation active at a time, and we only get one call stack. And again, the reason that we can use this global variable and why it's useful is that oftentimes, we are further, further down into the code base where we don't have access to the computation as a parameter. And so we need some way to get access to it, and Meteor gives us that way by giving us this global variable.

Another way to say this is that a computation can be set at some previous point in the call stack, and we don't know who set it exactly, but some other previous code is going to set it, and then eventually, we get up into our function. Another thing that we might do here is instead of getting the computation itself, we might just want to know if there is an active computation.

So at this point in the call stack, is there a computation that has been set at some previous point? So we can call another property called active, and active will be true if there is a computation that's active at this point. So let's just say is active and let this refresh.

And when I press Play here and get into the inner computation, notice down at the bottom it says is active, is true. Is active, is true. And so the reason that this property is true is because by the time we get here, a computation has been set. It was set previously down here. So this says C2. So this is sometimes useful to know whether or not you're in a reactive function, and you can perform some conditional logic depending upon whether the function is reactive or not.