Asynchronous Programming in Node.js
Now that you have a refreshed and updated idea of what JavaScript programming is really like, it’s time to get into the core concept that makes Node.js what it is: that of nonblocking IO and asynchronous programming. It carries with it some huge advantages and benefits, which you shall soon see, but it also brings some complications and challenges with it.
The Old Way of Doing Things
In the olden days (2008 or so), when you sat down to write an application and needed to load in a file, you would write something like the following (let’s assume you’re using something vaguely PHP-ish for example purposes):
$file = fopen('info.txt', 'r'); // wait until file is open $contents = fread($file, 100000); // wait until contents are read // do something with those contents
If you were to analyze the execution of this script, you would find that it spends a vast majority of its time doing nothing at all. Indeed, most of the clock time taken by this script is spent waiting for the computer’s file system to do its job and return the file contents you requested. Let me generalize things a step further and state that for most IO-based applications—those that frequently connect to databases, communicate with external servers, or read and write files—your scripts will spend a majority of their time sitting around waiting (see Figure 3.1).
Figure 3.1. Traditional blocking IO web servers
The way your server computers process multiple requests at the same time is by running many of these scripts in parallel. Modern computer operating systems are great at multitasking, so you can easily switch out processes that are blocked and let other processes have access to the CPU. Some environments take things a step further and use threads instead of processes.
The problem is that for each of these processes or threads is that there is some amount of overhead. For heavier implementations using Apache and PHP, I have seen up to 10–15MB per-process memory overhead in the past—never mind the resources and time consumed by the operating system switching that context in and out constantly. That’s not even 100 simultaneously executing servers per gigabyte of RAM! Threaded solutions and those using more lightweight HTTP servers do, of course, have better results, but you still end up in a situation in which the computer spends most of its time waiting around for blocked processes to get their results, and you risk running out of capacity to handle incoming requests.
It would be nice if there were some way to make better use of all the available CPU computing power and available memory so as not to waste so much. This is where Node.js shines.