Let asynchronous I/O happen by returning control back to the event loop

When a newcomer to Node.js first writes a file reading program, it comes out something like this.

1 var fs = 
require('fs');
2 
3 var fileContents;
4 
5 fs.readFile(filename, "utf8", function(err, data) {
6     fileContents = data;
7 });
8 
9 while (!fileContents) {
10     // wait until the file contents is filled
11 }
12 
13 // then print the data
14 console.log(fileContents);

But.. it does not work as intended. The program will never terminate. The while statement creates an infinite loop and halts everything. The file reading will never return its results. Nothing can proceed.

JavaScript in Node.js runs in a single thread

Trying to write the program like in the example is very understandable. In all of the popular web application languages and frameworks it would work. A Java or .NET programmer would expect here to be nothing wrong. In those environments a dedicated thread is allocated for program execution and other threads are easily created as needed to handle tasks elsewhere.

Things are fundamentally different in Node.js. In contrast to the mentioned popular languages, in Node.js your JavaScript program code runs in a single thread. There are never two pieces of JavaScript code running in parallel. This changes how things play together in a major way.

Code is run to completion

Each piece of JavaScript code is also always run to completion. There is no concept of temporarily suspending the execution of some JavaScript code to switch over to another piece of JavaScript code. This would be called preemptive multithreading. It is the way processes and threads work at the operating system level, but not in Node.js. This is the crucial missing link that renders the example broken.

To allow other parts of the program to proceed, you have to deliberately give control back. You have to let others use the only available thread. You have to play together with the other parts of the program.

Need to return control back to the system

For the single-threaded model to work a program needs to return back control to "the system". In Node.js this system is called the event loop and similar concept is present in browsers as well.

"Node.js is an asynchronous event driven framework."
(Node.js home page)

Node.js is closely related to events. As the definition suggests it is inherently driven by them.

The event loop is a huge while loop that is started in the node binary. It repeatedly takes incoming events and fires any event listeners subscribed to those events one at a time.

You need to let control back to this event loop. You can do it in several ways. You let control back to the system

  • by letting your top level JavaScript code reach its end
  • by letting your event listener JavaScript code reach its end
  • by using yield (ES6)
  • by using await (ES7)

And you need to do this frequently. The larger the amount of concurrency in your program the more important it is to frequently let other parts of the program to advance as well.

A working example

A correct working example would be simply

1 var fs = require('fs'
);
2 fs.readFile(filename, "utf8", function(err, data) {
3     console.log(data);
4 });

and nothing else.

The code sets up the call and returns control to one level up. After that, Node.js will take care of the rest and calls our function when the results are in. At that point we can print the data.

cover

There are 350 000 NPM packages. Your task: pick one.

Learn what to look for in a reliable NPM package.

Get Guide

Share article: