Maurício R Duarte
Maurício's Blog


Maurício's Blog

JS - The Event Loop

JS - The Event Loop

How does JS run async code if it's a single-threaded language?

Maurício R Duarte's photo
Maurício R Duarte
·Jan 5, 2023·

4 min read

Play this article

It's been a long time without posting anything here, but I'm back.
Let's talk about Javascript's Event Loop. As you may know, JS is a Single-Threaded language, so it can only execute 1 task simultaneously.
Assuming we have the Javascript Engine, a program built to execute Javascript code, we need to consider that each browser has a Javascript Engine:

  • Chrome and many other Chromium browsers have the V8 Engine

  • Safari has the JavascriptCore

  • Firefox has the SpiderMonkey

These engines can be different in implementation across browsers, but at least they contain two parts:

  • Memory Heap: A data store with memory allocation to store objects

  • Call Stack: It's a stack that keeps track of running functions. Every time the code is calling a function, it pushes that piece of code (stack frame) onto the stack. The stack frame will contain information about the functions and their local variables.

When a function is called, it goes onto the top of the call stack, and when that same function ends, it's popped out the call stack. LIFO (last-in-first-out) is the name given to this principle.

OK, now you've got the basics of the Call Stack. But, what would happen in the case of incrementing a setTimeout(foo, 2000) in the code?

JavaScript Runtime Environment

The JS Engine runs our code in this larger environment known as JavaScript Runtime Environment. It provides some web APIs to be able to access the browser's functions like DOM manipulation, timers, HTTP Requests, and so on.

But, what would happen in the case of incrementing a setTimeout(foo, 2000) in the code?

Task Queue

Web APIs move callbacks into the Task Queue, to wait for the Call Stack to be empty, and start executing that callback function. Then, the Task Queue is a data structure responsible for storing asynchronous callbacks to be moved to the Call Stack.
On the example above, you can notice that after the delay of 2000ms, the foo() function has been moved to the Task Queue, waiting to be executed when the Call Stack becomes empty.

This queue is also known as the "Macrotask Queue", "Callback Queue", or "Message Queue".

This overall process is known as Event Loop:

  1. Remove one task from the Task Queue

  2. Executing code until the Call Stack is empty

  3. Rendering any changes to the DOM

  4. Going to step 1

What about Promises?

After Promises have been introduced to the language, there's a new Queue in the picture, and that one is the Microtask Queue.

Microtask Queue

Known as well as Job Queue, the Microtask Queue is responsible for holding the callback function of Promises.
The known callback functions of Promises are .then(), .catch(), .finally(), and async/await .
They can be manually queued using the queueMicrotask() function.
Microtasks have more priority compared to Macro tasks because the entire microtask queue should be empty before Browser checks the Task Queue.

Then, with those new concepts in mind, we need to update our Event Loop workflow:

  1. Removing one task from the Task Queue

  2. Executing code until the Call Stack is empty

  3. Running all microtasks until the microtask queue is empty

  4. Rendering any changes to the DOM

  5. Going to step 1

For all of these steps, we have a name to point to all of them, and we call as Concurrency Model.


Big computations and slow functions can make the entire page unresponsive. When you fall into situations like these, you can try solutions like chunking, or even web workers.
The main idea behind chunking is to split big functions into smaller functions. You can achieve that using setTimeout for some iterations, forcing them to be chunked, and moving them to the end of the Task Queue.
I've got a friend that was used to saying: "There's nothing a setTimeout() can't fix".

I hope you've enjoyed this text. Please, press like and don't forget to share it with your colleagues or friends. See you!


Did you find this article valuable?

Support Maurício R Duarte by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
Share this