Understanding nodejs event loop without a Computer Science degree

Nodejs is asynchronous I/O, its high throughput makes it very popular nowadays. Although the idea is very appealing, but it doesn't means that your code will benefit it. Asynchronous programming is not just orgranise your code with callbacks. Callbacks could be synchronous too.

To understand nodejs asynchronous I/O, you need to ask yourself the following question:

How do you create an asynchronous function in nodejs? And how does it work?

To answer this question you need to understand event loop.
There are other useful resources on the internet, when reading these resources, you will see following terms: callstack, single thread, process.nextTick, setImmediate, I/O, polling, etc. If you don't have a CS degree, it may take you some time to understand what they are, and how they behave in async I/O.

In this article, I try to explain the idea of event loop without using any of these terms. Hope this will help you better understand event loop.

A translation business

You are running a business to translate document from English to Chinese. Your business strategy is to focus on large documents from a few large clients in order to have a stable income, and if time possible, take small document cases from public (could arrive at any time) to make extra profit. Your business just started, and you are the boss and the employee.

You can not work in an one-document-after-another way, because the translation of large document will take weeks or months. if you doing so, you will never have time to process the small documents.

So you decided to cut large documents to small pieces, put them into a todo list, and you process each piece from the todo list at a time. In this case, you have time to check the small cases when you finished translating a piece.

You want each piece to be independent, with complete context, so that when you translate it, you don't need to check other pieces. You use a smart way to do it:

Whenever you process a document in todo list, if the document have chapters, put all its direct chapters to the end of the todo list. Otherwise translate it.

IF document has chapters
  put all its chapters to the end of the todo list
ELSE
  translate the document 

In between two documents from the todo list (the short free time you have when you finished one document from todo list, before you take next one), you have time to check the small cases. If there are any, put all of them to the end of the todo list.

By keep doing this, you can serve both your large and small clients.

But there are still problems in this business process. Imagine one day is your big day, you have 80 new small cases, each takes you 1 hour to translate. You finished one document from todo list, you check the new small cases, and put them at the end of todo list. That's is to say, in the future, at some time, you will start to translate those 80 small documents one by one, which will take you in total 10 days (you work 8 hours a day). It blocks your large case for 10 days! You can image how angry your large client is.

You are a smart business man. You improved you business process: when you cut document in pieces, you don't just put them at the end of the todo list. You add an option to insert it before the first small case in todo list in order to translate it before any existing small cases in the todo list. You can't do this for all the cutting pieces, that will block the small cases too. So it is up-to-you to decide which piece to translate before existing small cases, and which piece to translate after existing small cases. (You can imagine that you make your decision by priority or urgence. Urgent document insert before small cases, otherwise append it at the end of todo list).

Insert the new piece from urgent large cases before any small cases in todo list

// snapshot of todo list
["Large", "Small", "Small", "Large"]
// the new piece from large document will be insert before the first small document piece in the todo list
["Large", "New Large", "Small", "Small", "Large"]

So we say, your business is reactive, you are only one person, but you can hanle multiple cases and make all your clients happy!

Back to Computer Science

Now you know how to run a translation business in a smart way. Let's go back to computer science world.

In computer science's vocabulary, you are the thread, or more specifiquely, the callstack. It can handle only one task a time. The document is your code, orgranised in function. The whole business is Javascript VM. As your business only have yourself to work, it is a single thread business. The todo list is a queue, you append new task at the end, pick the first one to process. Small cases from public represent the I/O.

When you put the piece of document into the end of the todo list to process it in the future, you use setImmediate() method to put a function at the end of the queue.

When you insert the piece of document before the small cases in todo list, you use process.nextTick() to make sure your function get called in the future but before any other I/Os.

Your business process is so called "event loop", because what it does is a loop:

event loop

// while true
//   get next task in queue
//   if task has sub-task
//     if setImmediate : move to end of queue
//     if process.nextTick: insert before I/O in queue 
//   else
//     run it.
//   check IO, push IO at the end of queue
// end

Answer to the question

How do you create an asynchronous function in nodejs? And how does it work?

In nodejs, we can use process.nextTick() and setImmediate() to create a asynchronous function. It will run in the future, one before I/O, another after I/O.

Notice

This page is not to demonstrate how nodejs actually works. The underlying implementation could use multiple queues, and different policies to schedule tasks. But the idea is the same: allocate a limited resources to satisfy the consumer's requirement in a timely manner.

Other resources

A good video about javascript event loop by Philip Roberts: What the heck is the event loop anyway?