JavaScript Event Loop And Call Stack Explained

JavaScript Event Loop And Call Stack Explained

Learn how JavaScript works in the browser. In this JavaScript tutorial, I explain how the call stack, event loop, job queue and more work together.

My goal with this article is to teach you how JavaScript works in the browser. Even though I've been working with JavaScript my whole career, I didn't get how these things work until recently.

I still forget how this works from time to time. That's why I wrote this article. I hope it will make you understand these concepts as well.


  • How JavaScript works in the browser
    • Call stack
      • Uncaught RangeError: Maximum call stack size exceeded
    • Heap
    • Web APIs
    • Callback queue
    • Event loop
      • setTimeout(fn, 0)
    • Job queue and asynchronous code
      • Promises: A quick recap
      • Where do promises fit in?
  • Conclusion

How JavaScript works in the browser

Before I dive into the explanation of each topic, I want you to have a look at this high-level overview that I created, which is an abstraction of how JavaScript interacts with the browser.

Don't worry if you don't know what all of the terms mean. I will cover each of them in this section.

High level overview of how JavaScript works in the browser

Note how most of the things in the graphic aren't part of the JavaScript language itself. Web APIs, the callback queue, and the event loop are all features that the browser provides.

A representation of NodeJS would look similar, but in this article, I'll focus on how JavaScript works in the browser.

Call stack

You've probably already heard that JavaScript is single-threaded. But what does this mean?

JavaScript can do one single thing at a time because it has only one call stack.

The call stack is a mechanism that helps the JavaScript interpreter to keep track of the functions that a script calls.

Every time a script or function calls a function, it's added to the top of the call stack. Every time the function exits, the interpreter removes it from the call stack.

A function either exits through a return statement or by reaching the end of the scope.

I created this small visualization to make it easier to understand:

const addOne = (value) => value + 1;
const addTwo = (value) => addOne(value + 1);
const addThree = (value) => addTwo(value + 1);
const calculation = () => {
  return addThree(1) + addTwo(2);

Note how each function call is being added to the call stack and removed once it finishes.

Every time a function calls another function, it's added to the top of the stack, on top of the calling function.

The order in which the stack processes each function call is following the LIFO principle (Last In, First Out).

The steps of the previous example are the following:

  1. The file loads and the main function is being called, which stands for the execution of the entire file. This function is added to the call stack.
  2. main calls calculation(), which is why it is added to the top of the call stack.
  3. calculation() calls addThree(), which again is added to the call stack.
  4. addThree calls addTwo, which is added to the call stack.


  1. addOne doesn't call any other functions. When it exits, it is removed from the call stack.
  2. With the result of addOne, addTwo exits as well and is being removed from the call stack.
  3. addThree is being removed as well.
  4. calculation calls addTwo, which adds it to the call stack.
  5. addTwo calls addOne and adds it to the call stack.
  6. addOne exits and is being removed from the call stack.
  7. addTwo exits and is being removed from the call stack.
  8. calculation can exit now with the result of addThree and addTwo and is being removed from the call stack.
  9. There are no further statements or function calls in the file, so main exits as well and is being removed from the call stack.

I called the context that executes our code main, but this is not how the official name of the function. In the error messages that you can find in the browser's console, the name of this function is anonymous.

Uncaught RangeError: Maximum call stack size exceeded

Maximum call stack error

You probably know the call stack from debugging your code. Uncaught RangeError: Maximum call stack size exceeded is one of the errors you might encounter. Below we can see a snapshot of the callstack when the error occured.

Follow the stack trace of this error message. It represents the functions calls that led to this error. In this case, the error was in the function b, which has been called by a (which has been called by b and so on).

If you see this specific error message on your screen, one of your function has called too many functions. The maximum call stack size ranges from 10 to 50 thousand calls, so if you exceed that, it's most likely that you have an infinite loop in your code.

The browser prevents your code from freezing the whole page by limiting the call stack.

I re-created the error with the following code. A way to prevent this is by either not using recursive functions in the first place, or by providing a base case, which makes your function exit at some point.

function a() {

function b() {


In summary, the call stack keeps track of the function calls in your code. It follows the LIFO principle (Last In, First Out), which means it always processes the call that is on top of the stack first.

JavaScript only has one call stack, which is why it can only do one thing at a time.


The JavaScript heap is where objects are stored when we define functions or variables.

Since it doesn't affect the call stack and the event loop, it would be out of the scope of this article to explain how JavaScript's memory allocation works.

Web APIs

Above, I said that JavaScript can only do one thing at a time.

While this is true for the JavaScript language itself, you can still do things concurrently in the browser. As the title already suggests, this is possible through the APIs that browsers provide.

Let's take a look at how we make an API request, for instance. If we executed the code within the JavaScript interpreter, we wouldn't be able to do anything else until we get a response from the server.

It would pretty much make web applications unusable.

As a solution to this, web browsers give us APIs that we can call in our JavaScript code. The execution, however, is handled by the platform itself, which is why it won't block the call stack.

Another advantage of web APIs is that they are written in lower-level code (like C), which allows them to do things that simply aren't possible in plain JavaScript.

They enable you to make AJAX requests or manipulate the DOM, but also a range of other things, like geo-tracking, accessing local storage, service workers, and more.

Callback queue

With the features of web APIs, we're now able to do things concurrently outside of the JavaScript interpreter. But what happens if we want our JavaScript code to react to the result of a Web API, like an AJAX request for instance?

That's where callbacks come into play. Through them, web APIs allow us to run code after the execution of the API call has finished.

What is a callback?

A callback is a function that's passed as an argument to another function. The callback will usually be executed after the code has finished.

You can create callback functions yourself by writing functions that accept a function as an argument. Functions like that are also known as higher-order functions. Note that callbacks aren't by default asynchronous.

Let's have a look at an example:

const a = () => console.log('a');
const b = () => setTimeout(() => console.log('b'), 100);
const c = () => console.log('c');


setTimeout adds a timeout of x ms before the callback will be executed.

You can probably already think of what the output will look like.

setTimeout is being executed concurrently while the JS interpreter continues to execute the next statements.

When the timeout has passed and the call stack is empty again, the callback function that has been passed to setTimeout will be executed.

The final output will look like this:

But what about the callback queue?

Now, after setTimeout finishes its execution, it doesn't immediately call the callback function. But why's that?

Remember that JavaScript can only do one thing at a time?

The callback we passed as an argument to setTimeout is written in JavaScript. Thus, the JavaScript interpreter needs to run the code, which means that it needs to use the call stack, which again means that we have to wait until the call stack is empty in order to execute the callback.

You can observe this behavior in the following animation, which is visualizing the execution of the code we saw above.

Calling setTimeout triggers the execution of the web API, which adds the callback to the callback queue. The event loop then takes the callback from the queue and adds it to the stack as soon as it's empty.

Multiple things are going on here at the same time. Follow the path that the execution of setTimeout takes, and in another run, focus on what the call stack does.

Unlike the call stack, the callback queue follows the FIFO order (First In, First Out), meaning that the calls are processed in the same order they've been added to the queue.

javascript web-development programming developer

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Why Web Development is Important for your Business

With the rapid development in technology, the old ways to do business have changed completely. A lot more advanced and developed ways are ...

Important Reasons to Hire a Professional Web Development Company

    You name the business and I will tell you how web development can help you promote your business. If it is a startup or you seeking some...

What Garbage Collection in JavaScript Is and How It Works

JavaScript values are allocated when things are created (objects, Strings, etc.) and freed automatically when they are no longer used. This process is called Garbage collection.

What Javascript Spread Operator is, How It Works and How to Use It

JavaScript spread operator is one of the more popular features that were introduced in ES6. This tutorial will help you understand it. You will learn what spread operator is and how it works. You will also learn how to use it to copy and merge arrays and object literals, insert data and more.

Top 10 Web Development Trends in 2020 | Web Development Skills

Top 10 Web Development Trends in 2020 will provide you with a detailed list of the trends which are currently being noticed. Upskilling to these trends will help you in landing onto better jobs in 2020-2021.