Skip to main content

Command Palette

Search for a command to run...

Speeding Up Script Loading: Async, Defer — and How HTML Parsing Behaves

How a small script tag can pause a whole page — and how to avoid it.

Updated
6 min read
Speeding Up Script Loading: Async, Defer — and How HTML Parsing Behaves
S
Passionate Full-Stack JavaScript Developer focused on building intuitive, scalable, and user-centric digital experiences using modern technologies.Specialized in the MERN stack (React, Node.js, and MongoDB) with practical experience architecting end-to-end web applications and seamlessly integrating intelligent AI features into modern platforms. I am highly comfortable handling both pixel-perfect frontend interfaces and robust backend logic.Driven by turning complex problems into elegant production-ready code. You can explore my full range of work, architecture patterns, and live deployments through my portfolio and GitHub links featured below.Outside the digital world, I recharge by exploring nature—especially during the monsoon season.

Imagine you're reading a storybook. Halfway through a page, the book's narrator pauses to fetch a messenger from another town, listens to what the messenger says, acts on it, and only then continues the story. That pause feels clumsy — and that's exactly how a browser can behave when it finds certain <script> tags. In this post, we'll follow that narrator: what makes the browser pause, how async and defer change the flow, and what that means for page load and events.

Quick overview:

Asyne and defer are boolean attributes which can be loaded along with the script tags. They are useful for loading external scripts into your web page.

When your browser encounters a <script> tag, it stops loading the rest of the HTML, fetches the JavaScript file and executes it before continuing. This can slow down your webpage, especially if the JavaScript file is large or the user's internet connection is slow.

To solve this problem, we have two helpful boolean attributes: async and defer

async attribute:

<script src="script.js" async></script>

When you add async to your <script> tag, the browser will download the JavaScript file while it continues to load the rest of the HTML. However, as soon as the JavaScript file is downloaded, the browser will stop loading the HTML, execute the script immediately and then continue loading rest of the HTML.

async is useful when your JavaScript file doesn’t depend on anything else on the page to run. For example, tracking scripts or Google analytics are good candidates for async since they don't need to interact with your page's content.

defer attribute:

    <script src="script.js" defer></script>

When you add defer to your <script> tag, the browser will download the JavaScript file while it continues to load the HTML, but it will not execute the script until the entire HTML is fully loaded and parsed. This means that all the content will be fully loaded before any of the defer scripts are run.

defer scripts are executed in the order in which they are defined.

Let's look at how HTML is processed and behaves.

Imagine you put a script tag inside the head tag without async or defer attributes. HTML starts reading from the top, line by line. When it finds a script tag, it stops. The script is then fetched and executed. During this time, HTML parsing is paused. Once the script runs, HTML parsing continues.

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./index.js"></script>
    // NOTE: HTML PARSING PAUSED HERE TILL SCRIPT EXECUTED
</head>

<body>
</body>

</html>

When script tag inside

Initially, the HTML parsing begins and completes smoothly because the script is placed at the bottom of the body tag. This means the entire HTML is parsed from top to bottom. Once finished, it encounters the script tag, and then the script fetching starts.

When <script async src=''..''> is inside the tag.

HTML parsing starts from the top. The moment script tags are encountered, HTML parsing won't stop. This happens in parallel; the script is fetched, and parsing continues. Once fetching is done, it's time for the script to execute. Only then does HTML parsing pause. Once execution is complete, HTML parsing continues.

In short: Fetching is independent, but execution is blocking.

Step-by-Step Breakdown (Following the Diagram)

  1. HTML Parsing: The browser starts reading your HTML code from top to bottom.

  2. Script Encountered: When it sees <script async src="...">, it starts fetching (downloading) the script in the background. Importantly, it does not stop parsing the HTML while downloading.

  3. HTML Parsing Paused: As soon as the script finishes downloading, the browser must stop parsing the HTML to execute the script immediately.

  4. Script Execution: The JavaScript code runs.

  5. HTML Parsing Continues: Once the script finishes running, the browser resumes parsing the remaining HTML.

<!DOCTYPE html>
<html>
<head>
    <title>Async Example</title>
    <!-- The browser downloads this while still reading the <body> below -->
    <script async src="analytics.js"></script>
</head>
<body>
    <h1>Hello World</h1>
    <p>The page keeps loading while the script downloads!</p>
</body>
</html>

When <script defer src=''..''> is inside the tag.

With "defer," HTML parsing continues when it reaches the script line. The script is fetched in the background, and HTML parsing doesn't stop. After HTML parsing is done, the script runs. "Defer" means all about non-blocking behavior, it allows the browser to keep building the webpage (parsing HTML) while the download the script in the background. meaning the script runs later, after the page is built. If you put it at the bottom of the body tag, it doesn't change anything. "Defer" delays the script, similar to a regular script tag.

Step-by-Step Breakdown (Following the Diagram)

  1. HTML Parsing Starts: The browser begins reading your HTML from top to bottom.

  2. Script Encountered: The browser sees <script defer src="...">.

  3. Background Fetching: Instead of stopping, the browser continues HTML Parsing and starts Script Fetching (downloading) at the same time.

  4. HTML Parsing Complete: The browser finishes building the entire DOM (the page structure).

  5. Script Execution: Only after the HTML is fully parsed does the script actually run.

<head>
  <!-- The script starts downloading immediately but waits to run -->
  <script defer src="app.js"></script>
</head>
<body>
  <h1 id="title">Hello World</h1>
  <button id="myBtn">Click Me</button>
</body>

// This works because 'defer' ensures the button exists before the code runs
document.getElementById('myBtn').innerText = "Updated by JS!";

The difference between async and defer boils down to when the browser pauses to run the script and what order it runs them in.

  1. The async Timeline Unlike mentioned defer diagram, async is "interruptive." As soon as the script finishes downloading, the browser stops parsing HTML to execute the script immediately.

  2. The Order Problem (Example) Imagine you have two scripts: LargeLibrary.js (a big file) and SmallScript.js (a tiny file that uses code from the big library).

Using async (Unpredictable)

<script async src="LargeLibrary.js"></script> <!-- 5MB -->
<script async src="SmallScript.js"></script> <!-- 1KB -->
  • What happens: SmallScript.js downloads in 10ms, while LargeLibrary.js takes 2 seconds.

  • The result: The browser runs SmallScript.js first because it's ready.

  • The crash: SmallScript.js tries to use a function from the library, but the library hasn't loaded yet. Error: Library is not defined.

Using defer (Guaranteed Order)

<script defer src="LargeLibrary.js"></script>
<script defer src="SmallScript.js"></script>
  • What happens: Both download in the background. Even though SmallScript.js finishes downloading first, it waits.

  • The result: The browser finishes parsing the HTML, then runs LargeLibrary.js, and then runs SmallScript.js.

  • The Success: Your code works perfectly because the order is preserved exactly as written in the HTML.

Summary

Use async for independent, non-order-sensitive external scripts; use defer for scripts that need the DOM and must preserve execution order; avoid plain blocking scripts in the head to keep the page responsive.