The JavaScript Event Loop Explained
Let’s dive into the JavaScript Event Loop.
You may have read before that JavaScript is asynchronous and single threaded. These two things sound counterintuitive, so how is this possible? The JavaScript language itself is single threaded. The asynchronous behavior is not a part of the JS language, but rather built on top of the core JS language in the browser and accessed through browser API’s.
Let’s define a few things first:
Heap — Objects are allocated in a heap, a heap denotes our memory.
Stack — The stack represents the single thread provided for the JS code execution.
Web APIs — These are built in browser functions that get sent off to different threads to process
Message Queue — After the Web API’s have completed, they get sent to the message queue to await until the call stack is empty.
JavaScript has a concurrency model that is based on an event loop.
If you didn’t know what a concurrency model was, like me, it is a model that specifies how threads in the system collaborate to complete the tasks they are given. These different models have various ways to handle splitting up the current tasks.
The JS event loop is a “run to completion” model. This offers us some insight into our code because we know that whenever a function runs, it will run entirely before any other code can run. The downside to this is that if a function takes too long to complete, our web application is unable to process any user interactions on the page, such as a click or scroll. This is called blocking the stack.
Your JS code runs single threaded. There is always just one thing happening at a time. In general, most browsers have an event loop for every browser tab. This ensures every process is isolated and will avoid a webpage with infinite loops or heavy processing from blocking your entire browser.
The call stack is a ‘Last In, First Out’ queue. The event loop continuously checks the call stack to see if there is a function that needs to run. While doing so, it adds any function call it finds to the call stack and executes each one in order.
Get nathan brickett’s stories in your inbox
Join Medium for free to get updates from this writer.
Lets take for example:
We see that our order will be 1, 2, 4, 3 because the function foo gets declared but does not actually get invoked until after our console.log(4).
So now let’s add in a twist.
Let’s add a setTimeout function which accepts two arguments. The first being a callback function and the second a time interval in milliseconds after which our function will be called.
Here we might be tricked into thinking our setTimeout function with a zero second time interval gets fired right away and 6 will come after 2 but do not be fooled! The correct order is going to be 1, 2, 4, 3, 6. 5.
What is going on here? A zero second delay won’t actually mean that the call back function will fire off after zero milliseconds. The execution of this function depends on the number of waiting tasks in the queue. Our setTimeouts callback function needs to wait until all of our other functions have run before they can execute. The delay given to our setTimeout function is the MINIMUM time required for the runtime to process our request, this is not a guaranteed time.
When setTimeout is pushed into the call stack, it is then sent to the Web API to begin the execution of the setTimeout timer. When the timer has finished, the Web API will send the callback function to the message queue to wait until the call stack is empty.
JavaScript has a message queue that handles certain functions such as our setTimeout function, all user-initiated events like click or keyboard events, fetch responses, and DOM events. The event loop gives priority to the call stack, and it will first process everything in the call stack before it begins to run the message queue.
The single thread of execution asks the runtime to perform an operation, providing a callback function and then it moves on to do something else. When this operation has been completed, the message queue will receive a message with an associated callback function. Then, at some point when the call stack is empty, this message will be dequeued and the callback function is sent to the call stack to be handled.
Let’s summarize what we have learned.
JS is single threaded but able to perform asynchronously by sending out certain functions to the browser utilizing built in Web API’s. With a message queue available in our event loop we are able to build our applications around a collection of asynchronous callback functions.Thus freeing the runtime to handle our current operations while we wait for the external events to happen.
Hope you learned something new about how JS is working under the hood!







