• Meteor

What Does it Mean to be Async?

From the class:  Meteor Fibers and Dynamics

In this video, we're going to review how the JavaScript runtime works, and specifically what it means to be asynchronous and how asynchronous I/O works in JavaScript. It's really important to understand this in order to understand how fibers work, and that's one of the key underpinnings of the media runtime.

Now, we cover this topic at length in the class, The JavaScript Runtime. So if any of this is confusing to you, you should go back and watch that class. We've got some code here. There's a function declaration. The function is called doLater. And then right below it, we call the function.

So this program is executing from top to bottom. And when we call the function doLater, the function gets pushed onto the call stack. So now there's going to be one entry here for doLater. And then inside of the doLater function, if it calls any additional functions, those will be pushed onto the call stack and the call stack will grow up like this. And we can see that in the Chrome debugger when we're debugging our code.

When the doLater function returns, the function is popped off of the call stack. And eventually, once all the functions have returned, the call stack is empty again. Then what the JavaScript Runtime will do is the event loop will look to see whether or not there's any functions in something called a task queue. And these are functions that are lining up waioting to execute as soon as the current call stack is empty.

So how can we get a function on to the task queue? Well, one of the first things we can use is a timer. So a timer allows us to execute some function at a later time. And it does that by using the task queue.

The first timer function we can use is SetTimeout. So calling the SetTimeout function will put another function onto the call stack. I'll just abbreviated it here. Oh, maybe I can fit it in. SetTimeout.

And what this will do is call into a native function in Node.js or in the browser that says take this function that we passed in as a parameter. And in 3,000 milliseconds or three seconds, put this function at the end of the line on the task queue. And then the function will return immediately, and that puts us back down here.

And then finally, everything will have returned and both of these functions will be popped off the call stack and it will be empty. Now, Node.js down here running below-- so this is machinery that's not in our program but that's running nonetheless-- in 3,000 milliseconds will take that function and put it at the end of the task queue line. So there's nothing in the task queue currently, so that will just go right in here at the beginning of the line.

And then on the next tick of this event loop, or the next time it loops around, it'll see that the call stack is empty and that there is a function ready in the task queue. So it'll call that function, and that function will start executing in the beginning of the call stack. So JavaScript is continually doing this. It's looping around and checking to see whether there's anything in this queue. And if so, pushing it onto the call stack and executing it.

And then as soon as the call stack is empty, it looks for the next item in the queue and does the same thing. It just keeps going while there are still items in the queue. So asynchronous means that the function is executing at a later time. And the way that that works is that the JavaScript runtime, or Node.js in this case, is putting that function onto the task queue later.

Now we can use the same machinery to do I/O whether it be reading a file or making a network request to MongoDB. Let's pretend, for example, that we're reading in a file so we use the Node.js FS module and call the readFile method, passing the path to the file and then a function callback that takes an error and a result as parameters.

So as soon as we call this function that puts a function onto the call stack. And what this does is it calls into the Node.js runtime. And what the Node.js runtime does is it starts a job to read in this file. So we're going to do some I/O here, and the I/O is going to be to read in a file.

And then it'll store away this callback function. And when the I/O work has done reading in the file, it can take the function and put it at the beginning of the task queue, just like we did with SetTimeout. Now, the difference between SetTimeout and I/O is that with I/O, we're actually doing some real work. And that's happening concurrently with the rest of our program.

So in other words, it's happening at the same time and the task queue can continue to be processed and our code can continue to execute while the operating system is reading in this file. So all of this is being handled concurrently by Node.js.

Now, once we have a file, the Node.js runtime will take this function and put it at the beginning of the queue. So we'll just put that here as an example. And when it makes its way to the front of the line, the event loop will pick it up and our callback inside of here will start to execute.

So this is also asynchronous in that this function here gets run at a later time. But because we're doing I/O work, reading from a file or making a network request, Node.js also makes that concurrent and so it's able to happen at the same time as the rest of our code. So other types of I/O down here that we could do would be making a request to Mongo, for example, or any kind of network request, TCP.

Or we could be writing a file and so on. And all of these can happen in parallel, and Node manages all of that for us automatically. So this is a simple method for concurrency because all we have to do is to call these functions, which are asynchronous, like readFile, and pass them in these callbacks. And the callbacks will just get executed at a later time when we have results.

And we don't have to worry about things like threading or multiple processes. So the whole idea here is to provide a simple way to get some concurrency between using the CPU and doing I/O. Being able to visualize this diagram will be useful in all sorts of tasks, but as we start talking about fibers I'd recommend that you keep this diagram in your head, and maybe even draw it out to help you see what's going on in programs that use fibers.