Event loop in JavaScript
- 01 Apr, 2022

JavaScript is a single-threaded language. That means that it can only do one thing at a time. But what does that mean in
practice?
Well, it means that if you have a long-running task, like a for loop that iterates over a million items, then you can’t
do anything else until that loop finishes.
You can’t click on a button, you can’t scroll the page, you can’t do anything. Your browser will be frozen until the
loop finishes. You can get angry and ask for the manager of the internet, but it won’t help. It’s just how JavaScript
works.
But wait a second. If JavaScript is single-threaded, then how can it handle multiple things at once? How can it handle
user interactions, network requests, and other things?
That’s where the event loop comes into play.
What is the event loop?
It sounds quite fancy, but it all boils down to a simple concept. JavaScript is a single-threaded language, so in order to handle multiple things at once, it needs to have a way to queue tasks. That’s the role of the event loop. A queue of tasks that need to be executed. Once the Call Stack is empty the next task is picked up from the queue. Simple as that.
Well, not really. It’s a bit more complicated than that, but we’ll get to that later.
How does the event loop work?
The Event loop is built on top of two main concepts: the Call Stack and the Event Queue.
Call Stack
The Call Stack is a data structure that stores information about the currently executing function. It’s a Last In, First Out (LIFO) data structure, which means that the last function that was pushed to the stack will be the first one to be popped from the stack.
Event Queue
The Event Queue is responsible for storing tasks that need to be executed. It’s a FIFO (First In First Out) data The Event Queue is responsible for storing tasks that need to be executed. It’s a First In, First Out (FIFO) data
Event Loop
The Event Loop is constructed from the Call Stack and the Event Queue. It’s a continuous loop that checks if the Call Stack is empty. If it is, then it takes the first task from the Event Queue and pushes it to the Call Stack. If the Call Stack is not empty, then it waits until it’s empty and then takes the first task from the Event Queue and pushes it to the Call Stack.
The general algorithm looks like this:
- JavaScript code is executed in a single thread, which is called the “main thread”.
- When an asynchronous operation is initiated, such as a network request, setTimeout, or Promise it is added to a queue of pending operations.
- The main thread continues executing other code while the asynchronous operation is being processed in the background.
- When the asynchronous operation is complete, a “callback” function is added to a separate queue, called the “event queue”.
- The Event Loop continuously checks the Event Queue for any functions that are ready to be executed.
- If there is a function in the Event Queue, the Event Loop moves it to the main thread and executes it.
- The main thread executes the function and continues executing any other code that is currently queued or pending.
- Steps 5-7 are repeated in a continuous loop, which is why it’s called the “Event Loop”.
It roughly boils down to a pseudo-code like this:
// main thread
while (true) {
// check for any pending asynchronous operations
if (pendingOperations.length > 0) {
// execute the next pending operation in the background
executeNextOperation();
}
// check for any callback functions that are ready to be executed
if (eventQueue.length > 0) {
// move the next function to the main thread and execute it
const nextCallback = eventQueue.shift();
executeOnMainThread(nextCallback);
}
}
The
algorithm
above is not taking into account the fact that the events are divided into Macro and Micro tasks. Macro tasks include things likesetTimeout
,setInterval
, andsetImmediate
, while Micro tasks include things likePromises
andprocess.nextTick
. For a more detailed explanation, you can refer to this article.
Things to keep in mind
There are a few important things to keep in mind when working with the Event Loop in JavaScript:
- Asynchronous code should always be written using callbacks or promises, to ensure that the Event Loop can properly manage the execution order.
- Care should be taken to avoid long-running operations in the main thread, as this can cause the application to become unresponsive.
- Code that is executed within a callback or promise should be written in a way that is “non-blocking”, meaning that it doesn’t perform any long-running operations that would cause the main thread to become blocked.
Conclusion
In summary, the Event Loop is a fundamental concept in JavaScript that allows for asynchronous programming and non-blocking I/O operations. It important to understand how it works in order to write efficient code that doesn’t block the main thread.