The JavaScript Event Loop – A Stack and a Queue

The JavaScript event loop is similar to .NET garbage collection in that we don’t need to learn the details of how it works, and therefore a lot of programmers don’t bother. As long as it does what we expect it to do – call callback functions when events fire, then it is tempting to ignore how it actually does its job. However, not only is learning how things work at a low level interesting, it can also have a subtle effect on the way we view our code. We begin to notice how particular statements will be interpreted and processed by our runtime, and this can sometimes lead to understanding a bug or problem more quickly. In Chad Fowler’s The Passionate Programmer, he encourages us to go deep in learning a technology for the sake of our careers. If you want to be known as a specialist in your niche, then you ought to know more than the average programmer. And an equally important if less practical benefit is that a deep understanding of a programming language can lead to a more sincere appreciation of its complexity, and its simplicity.

With that in mind, let’s take a look at the JavaScript event loop. First of all, what is it?

The JavaScript event loop is the mechanism through which the JavaScript runtime juggles function calls, events and callback functions. Like garbage collection, it is actually not all that complicated, and basically consists of a call stack and a message queue.

The Call Stack

If you are reading this article it is more than likely that you know what a call stack is. Still, let’s remind ourselves of its key features:

  • It is a LIFO (last in first out) collection, with two primary operations: ‘push’ and ‘pop’.
  • When a function begins its execution, a new frame is pushed onto the stack.
  • That frame is used to store local variables, parameters and a return address (of the calling function).
  • If function a calls function b, then a new frame for function b is pushed onto the stack above the frame for function a.
  • When function b completes, its frame is popped off, and execution continues from the return address stored in it.
  • So, if we have a function a which calls function b, then during the execution of function b, the stack will look like this:

call stack

JavaScript, like most programming languages, makes use of a call stack to manage function calls. And of course, JavaScript is built around callback functions. So when are these callback functions executed? How does a callback function find its way on to the call stack? To answer these questions, we need to look at the JavaScript message queue.

The Message Queue

In addition to the call stack, JavaScript also maintains a message queue for the specific purpose of handling events. If an event has an associated event handler (i.e. callback function), then when that event occurs a new message is added to the message queue, consisting of the event which has occurred along with the callback function which now needs to be executed. As we have seen, it is the call stack which processes function calls. Now here is a crucial bit:

A message is only moved from the message queue to the call stack when the call stack is empty.

That statement may seem fairly trivial, but it basically sums up the non-blocking, single-threaded nature of JavaScript. What it means is that once a function starts executing, nothing can interrupt it. Any callback functions which are ready to be processed have to wait. This is just how JavaScript works. A natural question then would be – what if the function currently on the call stack takes a long time? Wouldn’t this mean that the message queue could grow large, and important messages would be made to wait for a long time? This problem is avoided in JavaScript thanks to a second crucial fact, which I touched on last week:

I/O in JavaScript is non-blocking (asynchronous I/O).

There are a couple of things to say about this. Firstly, an assumption was made by the creators of JavaScript (and Node js), that time-consuming operations generally involve I/O. Consequently, a system of asynchronous I/O was implemented. This simply means that when an I/O operation needs to be performed, such as communicating with a server or a database, you need to pass in a callback function to be executed when the operation is complete. In other words, the calling function is not blocked. So assuming that it is indeed only I/O operations which are time-consuming, we will never experience the problem of messages in the queue waiting for a long time.

Is this a reasonable assumption? In most cases, yes. The only long-running operations I can think of which would not involve I/O would be processing an enormous array, or calling a recursive function many times. An example of the latter, computing Fibonacci numbers, is discussed here, including a discussion of some possible solutions to the problem.

The Event Loop

The call stack and message queue working together as described is referred to as the JavaScript Event Loop. The term “loop” is in reference to the runtime waiting for a message to appear in the queue, processing it, waiting again and so on.

The success of Node.js would suggest that this approach largely works, although I’m sure there are some cases, involving heavy mathematical processing for example, where it could be problematic.

Further Reading

http://blog.carbonfive gabapentin

Share Buttonvar hupso_services_t=new Array(“Twitter”,”Facebook”,”Google Plus”,”Linkedin”,”Digg”,”Reddit”);var hupso_background_t=”#EAF4FF”;var hupso_border_t=”#66CCFF”;var hupso_toolbar_size_t=”medium”;var hupso_image_folder_url = “”;var hupso_url_t=””;var hupso_title_t=”The JavaScript Event Loop – A Stack and a Queue”;
Share Button