The Node.js Concurrency Paradigm: Asynchronous I/O

Node.js relies on asynchronous Input and Output (I/O) in order to increase the computing efficiency and speed of an application. This article will take a basic look at what asynchronous programming actually means, and why asynchronous I/O is important in a language like Node.js.

For Node.js performance to be actualized, a certain programming paradigm must be followed. Node.js is an event-driven architecture, and relies on asynchronous (non-blocking) input/output calls, along with the Node.js event loop to gain performance. This means that calls made in Node.js to the file system, event loop, or any resource, must be done asynchronously.

Asynchronous programming, informally, means code that is not waiting for a call to return. This is done using callbacks and registering them with an event-listener.

Let’s take a look at the difference between a simple asynchronous (non-blocking) call versus a synchronous (blocking) call.

Simple Synchronous Example:

// synchronous (blocking)
var request = prepareData();

// twiddle thumbs waiting for response
// from remote web service
var response = getRemoteData(request);

// display results

First, notice the getRemoteData(request) call. If we’re on the client-side, and we make a synchronous call, the user’s browser is in a waiting state and frozen until the call is completed. The rest of the code cannot be executed until this call has returned.

If we’re on the server-side, and in Node.js we have an incoming request added to the event loop, but this request is a synchronous call, then we have a problem. If this synchronous call is to read or write to the file system, when executed, the entire event queue will be blocked and in a waiting state. No other calls in the event queue can be performed, because the server is halted and waiting for the blocking call to finish.

The speed and through-put of our application ultimately depends on how fast each synchronous call is completed. Since Node.js is single threaded the entire application will be blocked and waiting, which is obviously very bad.

Now let’s take a look at the same simple calls, but this time performed asynchronously.

Simple Asynchronous Example:

// Asynchronous (non-blocking)
var request = prepareData();

// call getRemoteData and continue to 
// execute next line of code
getRemoteData(request, function(response){ // pass a callback
  // display results when done

We begin here again by passing the request to getRemoteData(request, callback), but we also pass a callback as the second parameter. Remember we can do this because JavaScript supports functions as “first class citizens”.

Our inline anonymous callback function will fire, and the response will be passed as a dynamic argument when getRemoteData() returns.

What is important is our code continues to execute the line after getRemoteData() immediately. It does not wait for the call response to return. This is referred to as an asynchronous (non-blocking) call.

Programming in a purely asynchronous fashion is what makes Node.js so powerful.

Now, it is true that many mainstream languages support concurrency. And it is also true, that they also have asynchronous I/O support. However, the problem is that the majority of these languages have only concurrency and asynchronous “features”. In most languages, concurrency support was added long after the language’s release.

Node.js nearly only supports asynchronous programming and was developed purely with the asynchronous paradigm as a fore-front of its design.

And that’s it!
Happy asynch coding and JavaScript all the things!

The Responsibility of the Node.js Event Loop

Everyone always talks about the Node.js event loop and how its important, but what exactly is it responsible for?

Well… Let’s take a look! 😀

The event loop (event queue) in Node.js is a loop that is responsible for processesing incoming requests. This loop “contains” a queue of requests, and each request in the event loop is a callback.

Node.js Event Loop

The Node.js Event Loop

Since the Node.js event loop contains a queue, it processes the requests like a traditional queue First-In-First-Out (FIFO) fashion. Node.js will continue processing each callback in the event loop until there are no more callbacks left to process.

In order to do this, Node.js registers with the Operating System. When an request arrives, Node.js is alerted, and the event loop processes the request. Any other subsequent connections that are made are queued until Node.js has finished with the current request.

The event loop allows Node.js to scale efficiently by accepting (queueing) many requests at once. Efficient scaling is one of the benefits that Node.js (with the event loop) has over traditional web servers. The event loop is single-threaded. This allows Node.js (when used properly) to require far less computational resources than traditional web servers.

Traditional web servers like Apache or Nginx are multi-threaded. Multi-threaded web servers typically cannot scale to the magnitude that Node.js can without greatly increasing the system’s computing resources. Since multi-threaded web servers create a new thread for each incoming request, this increases both CPU and RAM requirements of the system for the same amount of traffic. This increase can be either positive or negative (depending on your application), but the amount of computing resources for multi-threaded web servers will be greater than the requirement for single-threaded ones.

And that’s it on the Node.js event loop for now!

Happy coding! 😀