How Browsers Read and Execute JavaScript Code

Understand how a browser loads, parses, compiles, and executes JavaScript. Learn the full pipeline from script tag to running code.

6 min read

Understanding how browsers execute JavaScript means tracing everything that happens between the browser seeing a <script> tag and your code actually running. This pipeline helps you write faster-loading code and debug problems.

The Full Pipeline

How a browser loads and executes JavaScript

Each step in this pipeline has a specific job. Let us walk through them one at a time.

Step 1: HTML Parsing and the Script Tag

The browser starts by downloading and parsing the HTML. It builds a tree-like structure called the DOM (Document Object Model) that represents every element on the page.

When the parser hits a <script> tag, it stops. By default, the browser pauses DOM construction, downloads the script, and runs it before continuing. This is why a slow-loading script can make a page appear blank.

htmlhtml
<!-- This blocks page rendering until script.js loads and runs -->
<script src="script.js"></script>
 
<!-- This downloads in parallel and runs when HTML is fully parsed -->
<script src="script.js" defer></script>
 
<!-- This downloads in parallel and runs as soon as it is ready -->
<script src="script.js" async></script>

defer is usually the right choice for scripts that interact with the DOM. The script downloads in the background while HTML parsing continues, then runs after the DOM is complete. async is good for independent scripts like analytics that do not depend on the page structure.

Step 2: Parsing, Source Code to AST

Once the script is downloaded, the JavaScript engine parses the text into an Abstract Syntax Tree (AST). An AST is a structured representation of your code that the engine can work with.

Take this line:

javascriptjavascript
const x = 2 + 3;

Before building the tree, the parser first breaks the line into individual tokens, each one labeled with what kind of syntax element it represents:

texttext
Keyword: const
Identifier: x
Operator: =
Number: 2
Operator: +
Number: 3
Punctuation: ;

These tokens are then arranged into a tree structure that represents the meaning: "declare a constant variable named x and assign it the result of adding 2 and 3."

If the parser finds invalid syntax, it throws a SyntaxError and the script does not run at all. This is why a missing parenthesis on line 100 can prevent line 1 from executing.

Step 3: Compilation, AST to Bytecode to Machine Code

Modern JavaScript engines use a technique called Just-In-Time (JIT) compilation. This happens in layers:

JIT compilation layers in a JavaScript engine

The interpreter starts by running code immediately from bytecode. When it detects a function being called many times (a hot path), the optimizing compiler recompiles that function into highly optimized machine code.

If the optimizer made assumptions that later turn out to be wrong, for example if it assumed a function always receives numbers but then gets a string, it deoptimizes back to baseline code. This is called a deopt, and it is why you should avoid changing the types of variables frequently.

Step 4: Execution

Once compiled, the code runs. The engine manages:

  • The call stack, which tracks which function is currently running
  • The memory heap, which stores variables and objects
  • The garbage collector, which frees memory that is no longer needed

JavaScript is single-threaded, meaning it does one thing at a time on the main thread. Long-running JavaScript blocks the page. This is why async patterns like timers, fetch, and animation frames are important: they schedule work for later without blocking the current task.

What Happens When There Is an Error

Syntax errors are caught during parsing. The script never runs.

javascriptjavascript
console.log("This will not print");
const x = ; // SyntaxError: Unexpected token

Runtime errors are different. They happen during execution rather than parsing, so any code before the error still runs normally, and only the code after it is skipped:

javascriptjavascript
console.log("This prints"); // This runs
undefinedFunction();         // ReferenceError: not defined
console.log("This does not"); // This never runs

The browser logs the error to the Console, including the line number and a stack trace showing the chain of function calls that led to the error.

Where Script Placement Matters

Where you put your &lt;script&gt; tags affects when your code runs and what it can access:

PlacementWhen It RunsCan Access DOM?Blocks Rendering?
Head, no attributesImmediately, before bodyNoYes
End of bodyAfter body parsedYesMinimal
Head, with deferAfter HTML fully parsedYesNo
Head, with asyncAs soon as downloadedMaybe notNo

The safest default for your own scripts is a &lt;script&gt; tag with the defer attribute in the head, or a plain script tag at the end of the body.

JavaScript Engines by Browser

BrowserEngineNotable For
Chrome, Edge, OperaV8Also used in Node.js, Deno, and Electron
FirefoxSpiderMonkeyFirst JS engine ever, created by Brendan Eich
SafariJavaScriptCore (Nitro)Also used in Bun runtime
Legacy IEChakraMicrosoft's engine, now retired

All engines implement the same ECMAScript specification. They produce the same results for valid JavaScript. They differ mainly in performance characteristics and which experimental features they support early.

For a deeper look at what happens after the browser loads JavaScript, read about how JavaScript works in the browser, which covers the event loop, call stack, and async execution. To practice running JavaScript in both environments, see how to run JavaScript in the browser and Node.js.

Rune AI

Rune AI

Key Insights

  • Browsers parse HTML and pause when they hit a script tag to load and run JavaScript.
  • The JS engine parses source code into an AST (Abstract Syntax Tree).
  • JIT compilation converts JavaScript to optimized machine code at runtime.
  • Place scripts at the bottom of the body or use defer to avoid blocking page rendering.
  • Different browsers use different JS engines (V8, SpiderMonkey, JavaScriptCore).
RunePowered by Rune AI

Frequently Asked Questions

Does JavaScript run while the page is loading?

By default, yes. When the browser encounters a script tag, it pauses HTML parsing, downloads and executes the JavaScript, then resumes parsing. This is why script tags are often placed at the bottom of the body or use the defer/async attributes.

What is just-in-time (JIT) compilation?

JIT compilation means the JavaScript engine compiles code while it runs, rather than ahead of time. It starts by interpreting code quickly, then identifies frequently executed code (hot paths) and compiles them to optimized machine code for faster execution.

Why do different browsers run JavaScript at different speeds?

Each browser uses a different JavaScript engine: Chrome uses V8, Firefox uses SpiderMonkey, Safari uses JavaScriptCore. They implement the same ECMAScript standard but have different optimization strategies, which leads to performance differences on certain operations.

Conclusion

When a browser encounters a script tag, it downloads the code, parses it into an abstract syntax tree, compiles it to bytecode and optimized machine code through JIT compilation, and finally executes it. This entire pipeline happens in milliseconds for most scripts. Understanding this process helps you write code that loads faster and avoid common performance pitfalls.