AniUI Academy

How JavaScript Runs Your Code

What happens inside the browser between your file arriving and something appearing on screen — the engine, the call stack, and why JavaScript does one thing at a time.

10 min read

Your JavaScript file arrives at the browser as plain text. A few milliseconds later, things are happening on screen. This lesson is about what fills that gap.

You can write code without knowing this. But you cannot debug it well, and one idea in here — that JavaScript does one thing at a time — explains a whole category of bugs that otherwise look like magic.

The engine

Every browser contains a JavaScript engine: a program whose entire job is reading your code and doing what it says. Chrome and Edge use one called V8, Firefox uses SpiderMonkey, Safari uses JavaScriptCore. Node.js uses V8 too, which is how the same language ended up on servers.

When your file arrives, the engine roughly does this:

  1. Step 1

    Reads the text

    Checks your code makes sense as JavaScript and turns it into a structure it can work with.

  2. Step 2

    Finds syntax errors

    A missing bracket is caught here — before a single line has run.

  3. Step 3

    Translates it

    Converts it into low-level instructions the machine can execute quickly.

  4. Step 4

    Runs it

    Executes those instructions top to bottom, and optimises the parts that run often.

Between your file arriving and something happening on screen.

One consequence of that second step is worth noticing. A syntax error — a missing bracket, a stray quote — stops the whole file before anything runs. Not "stops at that line": nothing at all runs, including the correct code above it. Beginners often assume the first half worked. It did not.

That is different from an error while running, where everything before the failing line has already happened.

Top to bottom, one line at a time

The engine reads your file from the top and works down. This sounds obvious and has a consequence people trip over constantly:

console.log(price); // ReferenceError: Cannot access 'price' before initialization
const price = 199;

You asked for price before the line creating it had run. The engine knows the name exists — it scanned the file first — but the value has not been set yet, so it refuses. Order matters.

The call stack

Programs are mostly functions calling other functions, and the engine has to remember where it was each time it goes into one. The call stack is how.

A stack is a pile. You add to the top and take from the top, like a stack of plates. When a function is called it is pushed onto the top; when it finishes it is removed, and the engine carries on from wherever it was underneath.

Take this:

function greet(name) {
  return "Hello, " + name;
}
 
function welcome() {
  const message = greet("Anish");
  console.log(message);
}
 
welcome();

At the moment greet is running, the stack looks like this:

Top — runs next

greet
welcome
main script
Bottom
Three things in progress. greet is on top, so it runs; the others are waiting beneath it.

greet returns, and it is removed. console.log is then pushed on, runs, and is removed. welcome finishes and is removed. The stack is empty, and the program is done.

This is also what an error message is showing you. A stack trace is the stack, printed top to bottom: the function that failed, then the one that called it, then the one that called that. Reading it from the top tells you exactly how your program arrived at the problem, which turns most debugging from guesswork into reading.

And if functions keep calling without ever returning, the stack grows until the browser gives up — Maximum call stack size exceeded. That message means a function is calling itself with no way out, and now you know precisely what it describes.

One thing at a time

There is exactly one call stack. That single fact is the most important idea in this lesson.

JavaScript is single-threaded: it does one thing at a time, and it finishes that thing before starting the next. There is no second stack running your code alongside the first.

Which means a slow piece of code blocks everything. If you write a loop that takes three seconds, then for three seconds the page cannot respond to a click, cannot update, cannot even repaint a hover. It is frozen — not crashed, just busy. You have met a page that did this.

So why do slow requests not freeze the page?

Here is the apparent contradiction. Fetching data from a server takes far longer than any loop, and yet pages stay perfectly responsive while it happens.

Because the waiting is not done by your code. Your code asks the browser to fetch something and says what to do when it arrives — and then it finishes and the stack empties. The browser does the waiting, on its own time, outside your program. When the answer comes back, it puts your "what to do next" onto the now-empty stack and it runs.

  1. Step 1

    Your code asks

    It requests the data and hands over a function to run once it arrives.

  2. Step 2

    Your code finishes

    The stack empties. Clicks, scrolling and repainting all work normally.

  3. Step 3

    The browser waits

    The network reply is handled outside your program entirely.

  4. Step 4

    Your function runs

    When the reply lands, the waiting function is put on the empty stack.

Why waiting is free, and why calculating is not.

That is asynchronous code, and it has a lesson of its own at the end of this track. For now, hold the distinction: waiting does not block, because the browser does it for you. Working does block, because only your code can do it.

Watch the order for yourself

This is the clearest demonstration of everything above. Predict the output before you run it — most people get it wrong the first time.

Try it yourself
Loading playground...

The numbers print in order, which is the surprise: the setTimeout is written before 3 but runs after it, even asking for a delay of zero.

Because it is not a delay of zero. It means "when the stack is empty, run this". Your remaining code runs first — always — and only then does the waiting function get its turn. There is no amount of zero you can ask for that jumps the queue.

What to take away

The engine reads your whole file first, so syntax errors stop everything before it starts. Code runs top to bottom. The call stack tracks which functions are in progress, newest on top, and printing it is what a stack trace does. There is only one stack, so slow code freezes the page — but waiting is handed to the browser, which is why network requests do not.

Next, you write and run your own code for the first time.

Check yourself

4 questions · pass 3/4 to unlock Your First Program

up to 50
  1. 1.What is the call stack?

  2. 2.Given this code, what prints and in what order? function a() { console.log("a"); b(); console.log("done"); } function b() { console.log("b"); } a();

  3. 3.What does it mean that JavaScript is single-threaded?

  4. 4.Why does the browser stay responsive during a slow network request, when it freezes during a slow loop?

4 left to answer