• JavaScript

Asynchronous Code

From the class:  The JavaScript Runtime

We've looked at a couple of ways to get a function onto the task queue and then have the event loop move that function into the call stack and start executing it when it gets to the front of the line. Two of those ways are by using setTimeout and setInterval. And they use a timer that is working in a runtime thread. So in other words, the timer is always running in the background. And when it hits the time, it puts the function onto the task queue. And the task queue just executes sequentially over time. And so we're working with the task queue synchronously.

There's other ways that a function can get onto a task queue that we've looked at, which is with events. Similar to with a timer, when you click on a button or something like that, there's a runtime thread in the background that's recording that event and then taking a function handler and putting it onto the task queue. One that we haven't looked at is if we make an Ajax call. So I'll just abbreviate using jQuery syntax here, and let's pretend that we're making an Ajax call.

Now in this case, we're still in the browser, but we're making an asynchronous call. And the work that's going to happen is going to happen with the browser. So the browser is actually going to go out and make the network request. And so in this case, we might be requesting some HTTP resource. And when we get a result back, it will take the callback function that we passed to our Ajax function up here and put that onto the task queue. So in this case, it's a little bit different than what's happening with a timer in that there's some real work that's being done. But the work is happening in parallel to what's happening in our application. So when you make a network request, it doesn't need to block everything else that's happening.

Now, it turns out on the server, if we use a library or framework like Node.js we can achieve the same kind of parallelism with file and network I/O. So I'll show you an example in a second, but let's pretend that we're reading a file, and we're on the server. So we call this function called readFile, and we give it the path of the file and then a callback function. Node.js can dispatch that to the operating system, and the operating system can do the I/O on the file.

So in other words, it can read the file, and when it's done, it'll notify the runtime to go ahead and put this call back function onto the task queue with the data for the file. And then once that callback makes it to the front of the line, we'll process the file data. So that process of reading or writing to a file is happening in parallel. So that's because the operating system is able to do it at the same time that our Node.js or our JavaScript application is running.

Now what exactly does I/O mean on the server? It basically means reading or writing to any file or making any network requests. So for example, if you're making a request to a database like Mongo or to SQL Server, that would be an I/O type of request. Or if you're reading or writing to just a regular text file or serving up an HTML page, that would be an I/O type of operation.

So let's go over and look at some code so you get a better sense of it. Now, this isn't a class in Node.js, but I'm going to use a little bit of Node here to demonstrate how we're able to open up this text file here called myfile.txt and do that asynchronously. In other words, allow the operating system to do the work, and when it's done with it, put a function onto the task queue.

So here we have app.js open, and let's just start writing some code. And again, don't worry too much about the Node.js. I'll explain what I'm doing. So what we're going to do is just require the file library, and that allows us to work with files on the server. Then I'll call the readFile function and pass the path to the file. So it's called myfile.txt. And then we'll pass a callback function that takes an error parameter and the data for the file. And what I'll do is just print out the data for the file, and we'll put a line above it so that we have some space.

Now, this is going to do something similar to what we did with setTimeout. It's going to put this function back on the task queue a little bit later. And if we wanted to make this a little bit easier to see on the server side call stack, we could say name the function so that we could see it. And then down here, this will actually get executed first because we're still on this part of the call stack. So we don't have any data yet from the file, and we're still completing this part of our code. So I'm going to say reading file.

And then we can hop over to the other console here, and I'm going to run this program directly by saying Node app, which is short for Node app.js. And let me just update this a little bit so that we can see the contents of the file. And so what's happening here is we print out first the reading file down at the bottom, and then this function gets queued up on the task queue once we have some data for the file, and then it prints out that data to the console.

And this happened in parallel. In other words, the reading of the file was done by the operating system, and that happened in parallel. And when we had the data, or when the operating system has the data, it takes this function and puts it back onto the task queue, and then this executes. So this makes writing a concurrent code with JavaScript and Node.js pretty straightforward because we can just use the normal callbacks that we're used to using with all of the I/O functions of Node. And it uses the same task queue mechanism and event loop that are used for normal GUI events and the setTimeout and setInterval timer helpers.

Now, one thing that has confused me in the past is knowing whether something is going to execute immediately, or whether it'll be put on the task queue for execution later. So in this case, this function is going to be executed later, and then this function will be executed right away. But let's just say that this was-- we had an array. So I'll just create a variable called array. And now what we want to do is to iterate over the array using the for each function.

Now in this case, we're passing a call back just like we do for the asynchronous functions. And what I'll do is just print out the item. But this code is executing synchronously. And what I mean by that is if I come down here and print to the log and run this again, the one, two, three, each item is printed first, and then now what is printed. And that's because this function here is executed right away, one time for each of the items in this list. So it's actually executed synchronously.

So the thing to remember is if you're on the server and you're doing anything related to files or networking, those functions or that work will be done asynchronously, and the functions will be executed on the task queue. But if you're iterating over items on a list, or you're passing a call back to a function, chances are it's happening synchronously. But you'll have to read the documentation for specific functions to know for sure. So in this video, we explored a bit more what it means to be asynchronous and how we can do parallel work and put callback functions back on the task queue when the work is complete.