Asynchronous Programming
To understand how JavaScript achieves asynchronous processing, we first need to grasp the notion of functions as first-class citizens.
JavaScript treats functions as first-class citizens. Functions are objects; hence, they can be assigned to variables, passed in as arguments to other functions, and returned by functions. JavaScript leverages the passing of functions as arguments to achieve asynchronous processing.
In a traditional web platform, an application blocks when it needs to process I / O operations, e.g., reading from a database. Any other operation has to wait until I / O processing has finished. However, Node.js makes it possible for applications to have non-blocking I / O processing. Thanks to its asynchronous nature, a Node.js application can still process other operations as they come.
We'll illustrate both blocking and non-blocking I / O with two simple examples. Let's assume we have a file named "random-file.md" with the following content:
File can be read synchronously
File can be read asynchronously
Here is a code example to process the file content in a synchronous fashion:
const fs = require('fs');
console.log('About to read file content');
try {
let content = fs.readFileSync('random-file.md', 'utf-8');
console.log(content);
console.log('Done reading file');
} catch (err) {
console.log(err);
}
console.log('Node.js is based on JS.');
Source: https://gist.github.com/charlenetshos/733f4b96c4a38d89b55279bbbeb123cb
In the above code snippet, Node.js will be blocked until the file reading has been completed. So the console will log the following output:
About to read file content
File can be read synchronously
File can be read asynchronously
Done reading file
Node.js is based on JS
Now, let's rewrite the example to process the file asynchronously.
const fs = require('fs');
console.log('About to read file content');
fs.readFile('file.md', 'utf-8', function (err, content) {
if (err) {
return console.log(err);
}
console.log(content);
console.log('Done reading file');
});
console.log('Node.js is based on JS');
Source: https://gist.github.com/charlenetshos/9316c378ae28bf57ba2ee04bcf6d21c7
If you run the above code snippet, you'll notice that the console will log "Node.js is based on JS" before the file content.
About to read file content
Node.js is based on JS
File can be read synchronously
File can be read asynchronously
Done reading file
That's because Node.js is not waiting for file reading to complete before processing other operations.
Event Loop
The event loop runs tasks in a single main thread and continuously collects polls and callbacks from the event queue, one at a time. It typically delegates blocking I / O tasks to a threads pool, and each task has a callback function with operations that need to happen when the I / O task is done.
So in a typical Node.js server, the event loop gets multiple requests and processes them one at a time as they come out of the event queue. Each request represents an event and has a callback function responsible for sharing the response with the client once the request has finished its course. The event loop executes operations for each event and delegates I / O-intensive tasks to the threads pool. Each I / O task has its callback. The event loop goes through each callback from the event queue. When it gets to the callback to return an HTTP response, it executes it, and the client receives a response.
Hence a Node.js server can run on a single thread.