Asynchronicity in Node.js

One of the biggest advantages of using Node.js over Python or Ruby is that Node has a non-blocking I/O mechanism. To illustrate this let me use an example of a line in a Starbucks coffeeshop. Let’s pretend that each person standing in line for a drink is a task, and everything behind the counter — cashier, register, barista — is a server or server application. When we order a cup of regular drip coffee, like Pike, or hot tea, like Earl Grey, the barista makes it. While the whole line waits while that drink is made, and the person is charged the appropriate amount…

Non-Blocking I/O

One of the biggest advantages of using Node.js over Python or Ruby is that Node has a non-blocking I/O mechanism. To illustrate this, let me use an example of a line in a Starbucks coffee shop. Let’s pretend that each person standing in line for a drink is a task, and everything behind the counter — cashier, register, barista — is a server or server application. When we order a cup of regular drip coffee, like Pike, or hot tea, like Earl Grey, the barista makes it. The whole line waits while that drink is made, and the person is charged the appropriate amount.

Asynchronicity in Node.js
Asynchronicity in Node.js

Of course, we know that these kinds of drinks are easy to make; just pour the liquid and it’s done. But what about those fancy choco-mocha-frappe-latte-soy-decafs? What if everybody in line decides to order these time-consuming drinks? The line will be held up by each order, and it will grow longer and longer. The manager of the coffee shop will have to add more registers and put more baristas to work (or even stand behind the register him/herself). This is not good, right? But this is how virtually all server-side technologies work, except Node. Node is like a real Starbucks. When you order something, the barista yells the order to the other employee, and you leave the register. Another person gives their order while you wait for your state-of-the-art eye-opener in a paper cup. The line moves, the processes are executed asynchronously and without blocking the queue by waiting.

This is why Node.js blows everything else away (except maybe low-level C/C++) in terms of performance and scalability. With Node, you just don’t need that many CPUs and servers to handle the load.

Asynchronous Way of Coding

Asynchronicity requires a different way of thinking for programmers familiar with Python, PHP, C or Ruby. It’s easy to introduce a bug unintentionally by forgetting to end the execution of the code with a proper return expression.

Here is a simple example illustrating this scenario:

var test = function (callback) {
  return callback();  
  console.log('test') //shouldn't be printed
}

var test2 = function(callback){
  callback();
  console.log('test2') //printed 3rd
}

test(function(){
  console.log('callback1') //printed first
  test2(function(){
  console.log('callback2') //printed 2nd
  })
});

If we don’t use return callback() and just use callback() our string test2 will be printed (test is not printed).

callback1
callback2
tes2

For fun I’ve added a setTimeout() delay for the callback2 string, and now the order has changed:

var test = function (callback) {
  return callback();  
  console.log('test') //shouldn't be printed
}

var test2 = function(callback){
  callback();
  console.log('test2') //printed 2nd
}

test(function(){
  console.log('callback1') //printed first
  test2(function(){
    setTimeout(function(){
      console.log('callback2') //printed 3rd
    },100)
  })
});

Prints:

callback1
tes2
callback2

The last example illustrates that the two functions are independent of each other and run in parallel. The faster function will finish sooner than the slower one. Going back to our Starbucks examples, you might get your drink faster than the other person who was in front of you in the line. Better for people, and better for programs! :-)

Author: Azat

Techies, entrepreneur, 20+ years in tech/IT/software/web development expert: NodeJS, JavaScript, MongoDB, Ruby on Rails, PHP, SQL, HTML, CSS. 500 Startups (batch Fall 2011) alumnus. http://azat.co http://github.com/azat-co

5 thoughts on “Asynchronicity in Node.js”

  1. @Aleksey,

    The examples above were made very simple on purpose to illustrate the approach. If you (or somebody else) want to dig deeper into parallelism and concurrency, here is a good discussion: http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference. According to their definition you might be wrong and examples are neither concurrency nor parallelism.

    As for the rest of your comment: because Node.js has superior support for asynchronous programming doesn’t mean developer can’t write synchronous code. Hence the examples. You statement that Node.js is not unique and every single popular modern language out there supports non-blocking IO is simply not true. Ruby and Python don’t support non-blocking IO out of the box, by default, without EventMachine or Twisted frameworks. Node.js is not a framework. That’s a huge different in learning curve, robustness and usability.

  2. First, almost every single popular modern language out there supports non-blocking IO. Node.JS is not unique, nor innovative in this aspect.

    If you’d say Node somehow mandates asynchronicity (thus blows away other platforms), you’d be wrong, because it actually doesn’t. It promotes asynchronicity, but that’s about it. There are ton of popular libraries that block here and there – for example, most templating engines and many parsers do. And even network-related libraries (mostly, external library bindings) sometimes tend to block. For example node-db-drizzle calls drizzle_query (which blocks until server starts sending a reply), and it never even sets DRIZZLE_NON_BLOCKING flag on a connection.

    Anyway, the point just is, Node.JS isn’t special at all. It’s not really better nor worse than others, and you could equally easily do the same tricks on other platforms with approximately the same level of success.

    Second, please do not mistake concurrency with parallelism. Functions in your last example do NOT run in parallel. They just set up two timers and return the control to main event loop, which calls them on occasion (but completely sequentially).

  3. @Jared,

    Although there are multiple solutions for Python, e.g., Twisted, and Ruby, e.g., EventMachine, they are inferior to Node.js in performance, complexity and other things. Node was build from the ground up to support non-blocking I/O model. JavaScript was third re-incarnation of Node. EventMachine is an addition to Ruby, Twisted will take months for experienced Python developer to master. Node will take 2 days. To create a server in Node takes only 2 lines.

    Follow full discussion on Google Plus: https://plus.google.com/116816240985852388027/posts/hH4iQWzGpM7

  4. Evented programming is fantastic for IO-bound workloads, but you can get this in any C-based scripting language, by using a framework that binds libevent or libev under the hood. You might want to check out Twisted (Py) or EventMachine (Ruby). Tons of good stuff to explore :-)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.