Skip to main content

Command Palette

Search for a command to run...

How does JavaScript work? πŸ€”

Updated
β€’10 min readβ€’View as Markdown
How does JavaScript work? πŸ€”
G

Hello! I'm a dedicated software developer with a passion for coding and a belief in technology's impact on our world. My programming journey started a few years ago and has reshaped my career and mindset. I love tackling complex problems and creating efficient code. My skills cover various languages and technologies like JavaScript, Angular, ReactJS, NodeJs, and Go Lang. I stay updated on industry trends and enjoy learning new tools. Outside of coding, I cherish small routines that enhance my workday, like sipping tea, which fuels my creativity and concentration. Whether debugging or brainstorming, it helps me focus. When I'm not coding, I engage with fellow developers. I value teamwork and enjoy mentoring newcomers and sharing my knowledge to help them grow. Additionally, I explore the blend of technology and creativity through projects that incorporate art and data visualization. This keeps my perspective fresh and my passion alive. I'm always seeking new challenges, from open-source contributions to hackathons and exploring AI. Software development is more than a job for meβ€”it's a passion that drives continuous learning and innovation.

"Last updated: September 2026"

Did you know the simple statement of JavaScript needs a lot of work done behind the seen to get it executed?

JS Engine.png

Hmm… So the browser doesn’t understand javascript directly. Then how are we going to ask the browser to do something?

Let’s start with what language the browser understands. The browser only understands 0s and 1s language i.e. Statements in Binary/Bits format.

We can’t convert our whole JavaScript into bits easily. So what should we do now? πŸ€”

JavaScript engine:- β€œHey don’t worry I can help you with that give me your JavaScript fileβ€œ.

What is a JavaScript engine then?

So how does the engine turn our code into something the machine runs?

The engine reads the whole file first and builds an Abstract Syntax Tree β€” a tree that represents the structure of our code. That tree goes to an interpreter (in V8 it is called Ignition), which turns it into bytecode and starts executing straight away. This is why JavaScript starts running almost instantly: nothing waits for a full compile.

While that bytecode runs, the engine watches it. If a function gets called again and again with the same kinds of values, the engine marks it as hot and hands it to an optimising compiler (TurboFan). That compiler produces real machine code, specialised for the types it has been seeing.

If the assumption later turns out to be wrong β€” say a function that always received numbers suddenly gets a string β€” the engine throws the optimised version away and falls back to bytecode. This is called deoptimisation.

function add(a, b) {
  return a + b;
}
 
// Called thousands of times with numbers β†’ V8 compiles a fast, number-specific version
for (let i = 0; i < 100000; i++) {
  add(i, i + 1);
}
 
add("hello", "world");  // types changed β†’ the optimised version is discarded

This is the practical takeaway from all of it: keep the types going into a function consistent. Not because "JavaScript is slow", but because switching types forces the engine to undo work it had already done for you.

Now browser can understand this machine code and behave accordingly.

Here are some JS engine examples.

Where JavaScript runs Engine
Chrome, Edge, Opera, Brave V8
Firefox SpiderMonkey
Safari JavaScriptCore (Nitro)
Node.js V8
Deno V8
Bun JavaScriptCore

Edge used to have its own engine, Chakra. Microsoft rebuilt Edge on Chromium in 2020, so it runs V8 now β€” which is why "works in Chrome, broken in Edge" is a much rarer bug report than it used to be.

Correction:-

With the Edge 79 release, Microsoft is switching to a Blink browser engine with a V8 JavaScript engine.

Both Blink and V8 are developed under Chromiumβ€”an open-source project with an open-source web browser of the same name. Chromiumβ€”the open-source browserβ€”is used by Google for its own Chrome browser. Now, Microsoft's Chromium Edge will do the same.

So what is inside this javascript engine?

Here is a very basic view of JavaScript Engine.

Untitled Diagram (5).png

Memory heap

JavaScript engine is sometimes unable to allocate memory at compile-time, so variables that allocated at runtime go into memory heap (unstructured region of memory). Data/Objects that we allocate in the heap section exist even after we exit the function which allocated the memory inside the heap.

Here we face a major problem of memory leak.

So what is a memory leak?

A memory heap has limited space. If we keep using heap space without caring about freeing up unused memory. This causes a memory leak issue when there is no more memory available inside the heap.

To fix this issue javascript engine introduced a Garbage collector.

What is a Garbage collector?

Garbage collection is a form of memory management. It’s like a collector which attempts to release the memory occupied by objects that are no longer being used. In other words, when a variable loses all its references Garbage collection marks this memory as β€œunreachable” and releases it.

Execution context stack

A stack is the data structure that follows the Last In First Out (LIFO) principle (the last item to enter the stack will be the first item to be removed from the stack).

ECS stores execution context for all the functions. Execution context is defined as an object which stores local variables, functions, and objects.

In simple words, each function is pushed on the top of the sack. JavaScript engine executes the function which is at the top of this stack.

As JavaScript engine has only one ECS, it can execute only one thing at a time which is at the top of the ECS. This is what makes JavaScript single-threaded.

You must have heard of stack overflow.

What does that mean? - ECS also has limited space. So, if we keep adding function on the top of the stack. At some point, there will not be more space to add more stack frames. At this point, we get a stack overflow error.

Consider the following example.

function heyJS() {
	console.log("Hello you are awesome!!!!");
	heyJS();
}
heyJS();
stack.png

Well, that went into an infinite recursion and we have a stack overflow error.

Screenshot 2020-10-20 115250.png

So as I mentioned JavaScript is a simple threaded language, which means it has only one call stack ad therefore it can only execute one statement at a time.

Wait, we also heard about asynchronous programming in javascript. So how does that work when only one task is allowed at a time?

Here comes Web API’s and Callback queue.

Web API’s

Web APIs are not part of the JS engine but they are part of the JavaScript Runtime Environment which is provided by the browser. JavaScript just provides us with a mechanism to access these API’s. As Web APIs are browser-specific, they may vary from browser to browser. There may be cases where some Web APIs may be present in one browser but not in another.

Examples:-

document.getElementById();
document.addEventListerner();
setTimeOut();
setInterval();

Example:-

console.log(β€œFirst!”);

setTimeout(() => {
	console.log(β€œSecond!”);
}, 1000 );

console.log(β€œThird!”);
/*
OutPut:- 
First
Third
Second
*/

It’s weird, right?

β€œSecond” is inside the setTimeout so that will executed after 1 second.

What exactly happens behind the scene?

Untitled Diagram (4).png Untitled Diagram (9).png Untitled Diagram (7).png

After 1-second WebAPI will get notified, hey you have code that you need to execute now. WebAPI β€œOh it’s console.log() I need to execute that, but I can’t execute this directly. Let’s send it to Callback Queue” *β€œHey, Queue here is the callback please add this on your list and execute it”. *

Callback Queue

Callback Queue or Message Queue is a queue data structure that follows the First In First Out principle (item to be inserted first in the queue will be removed from the queue first). It stores all the messages which are moved from the event table to the event queue. Each message has an associated function. The callback queue maintains the order in which the message or methods were added in the queue.

Event loop

The event loop continuously checks if the execution context stack is empty and if there are any messages in the event queue. It will move the method from the callback queue to ECS only when the execution context stack is empty.

Callback Queue

β€œHey, Event loop please check if ECS is empty. I have some callbacks that you need to push into ECS”.

Event Loop

β€œQueue please give me callbacks ECS is empty now, I will push them on the stack to execute them.”

Untitled Diagram (8).png

And finally, in the end, we will get out output.

// First
// Third
// Second

There are two queues, not one

Here is where the picture we have drawn so far is incomplete. The engine does not have one queue of pending callbacks. It has two, and they do not get equal treatment.

The task queue (you will also see it called the macrotask or callback queue) holds callbacks from setTimeout, setInterval, I/O, and UI events like clicks.

The microtask queue holds callbacks from promises β€” .then(), .catch(), .finally(), anything after an await β€” plus queueMicrotask() and MutationObserver.

The rule that decides everything:

When the call stack empties, the event loop drains the entire microtask queue. Only then does it take one callback from the task queue. Then it drains microtasks again.

Microtasks jump the line. Every time.

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Call Stack  β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
           β”‚ empty?
           β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  Microtask queue  β”‚  ← drained COMPLETELY
 β”‚  .then  await     β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚ empty?
           β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚    Task queue     β”‚  ← ONE callback taken
 β”‚ setTimeout  click β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           └──── back to the top

Let's watch it happen:

console.log("1 β€” script start");
 
setTimeout(() => console.log("2 β€” setTimeout"), 0);
 
Promise.resolve().then(() => console.log("3 β€” promise then"));
 
queueMicrotask(() => console.log("4 β€” queueMicrotask"));
 
console.log("5 β€” script end");

Output:

1 β€” script start
5 β€” script end
3 β€” promise then
4 β€” queueMicrotask
2 β€” setTimeout

Walk through it:

  1. "1 β€” script start" runs immediately β€” it is on the call stack.

  2. setTimeout hands its callback to the Web API. After 0ms it lands in the task queue.

  3. The promise is already resolved, so its callback goes straight to the microtask queue.

  4. queueMicrotask puts its callback in the microtask queue, behind the promise one.

  5. "5 β€” script end" runs. Now the script is finished and the call stack is empty.

  6. The event loop drains the microtask queue first: 3, then 4.

  7. Only now does it take from the task queue: 2. That setTimeout(fn, 0) waited for two promise callbacks that were queued after it. The 0 never meant "immediately" β€” it means "after the current work, and after every microtask".

Microtasks can starve the page

Because the loop drains the entire microtask queue before doing anything else, a microtask that queues another microtask never lets go:

function loop() {
  Promise.resolve().then(loop);   // do not run this
}
loop();

This freezes the tab. The call stack never overflows β€” each .then() gets a fresh stack β€” but the event loop never reaches rendering or the task queue again. A setTimeout recursion does not do this, because each iteration goes to the back of the task queue and gives the browser a turn.

Single-threaded, so how do we do real work?

The engine has one call stack, so heavy synchronous work blocks everything β€” including rendering. Sorting a hundred thousand records freezes the UI, and no amount of async fixes it, because async does not create a thread.

For genuinely heavy computation there are Web Workers: a separate thread with its own engine instance and its own stack. They cannot touch the DOM, and you talk to them by passing messages, but the main thread stays responsive while they work.

We will go much deeper into all of this β€” promises, async/await, and how the two queues behave under real code β€” later in this series.

This is just an overview of how JavaScript Engine works.

JavaScript engine is way more complex than how we discuss here today.

I will try to get deeper into the JavaScript engine in some of my future Articles.

In the next article of this series, I will explain Javascript Types, values, and variables.


Hope you like it, if yes **like & share.**

Thanks for your time.

Happy coding….

← Introduction to JavaScript Types, values, and variables in JavaScript β†’*

T

Ganesh Jaiwal,

Thanks for sharing.. Very well explained, just like the story telling..Awesome.

G

Thankyou. I am glad that you liked it. 😊

F

Simple and short explanations. Thanks for sharing πŸ‘

1
G

Thankyou 😊

G

Hey, nice point of view and expression. I read it with pleasure. Keep writing.

8
G

Thankyou Gizem, I am glad that you like this article. I love to explain that I learn.

1

JavaScript - Basics to Advance

Part 2 of 12

This series will cover everything you need to know about JavaScript from basics to advanced. Please stick with us and take your JS code quality and productivity to another level #JavaScript #JS

Up next

Types, values, and variables in JavaScript

"Last updated: September 2026" In this article, we will take an overview of types, values, and variables in JavaScript. A compuer program can simply be explained as a piece of code that manipulates so

More from this blog

H

Hacker's Haven

28 posts

I'm a Senior Software Developer with expertise in programming languages and tech. Follow me for detailed articles and tutorials on software development tools and frameworks.