Skip to main content

Command Palette

Search for a command to run...

Async and Defer Execution Order

Updated
5 min read
Z
I’m a Frontend Engineer with 6+ years of experience building fast, scalable, and maintainable web applications. I focus on turning complex requirements into clear, reliable user experiences using modern frontend technologies.

Before we deep dive, we need to know some differences.

1. The Main Thread is "Busy," Not necessarily "Blocked"

Parsing HTML is the primary job of the main thread when a page first loads. While the browser is parsing HTML, it is actively working. If you have a massive HTML file (e.g., several megabytes of text), the main thread will be occupied with parsing, which can delay the browser's ability to respond to user input (like clicks or scrolls). In that specific sense, the main thread is "busy" and cannot do other things.

2. The "Preload Scanner" (Off-Thread Help)

Modern browsers use a secondary, lightweight thread called the Preload Scanner (or speculative parser).

  • While the Main Thread is busy parsing HTML or waiting for a script to execute, the Preload Scanner "peeks" ahead at the rest of the HTML.

  • It looks for external resources like images, CSS, and scripts.

  • It starts downloading these resources in the background (off the main thread) so they are ready by the time the Main Thread reaches them.

How a regular script (no async, no defer) runs?

<script src="app.js" />

When a browser encounters a regular <script> tag (without async or defer), it treats it as a synchronous, render-blocking operation.

1 HTML Parsing & DOM Construction : The browser's Main Thread begins reading the HTML document byte-by-byte. It converts these bytes into tokens (like <html>, <body>, etc.) and starts building the DOM (Document Object Model) tree.

2 Script Discovery & The "Block": As soon as the tokenizer encounters a <script src="..."> tag, the HTML parser stops immediately.

  • The Problem: The browser doesn't know if the script will use document.write() or modify the DOM structure that follows.

  • The Result: DOM construction is completely paused. The Main Thread cannot move to the next line of HTML.

3 The Speculative Preload Scanner: While the Main Thread is blocked, modern browsers (like Chrome or Firefox) don't sit entirely idle. A secondary thread called the Preload Scanner "peeks" ahead at the rest of the HTML.

  • It looks for other external resources (CSS, images, or other scripts).

  • It starts downloading them in the background so they are ready by the time the Main Thread gets to them later.

4 Network Fetching: The browser issues a network request to download the script file. During this time, the Main Thread remains blocked from parsing HTML. If the network is slow or the file is large, the user sees a "frozen" page load.

5 Script Execution: Once the script is fully downloaded, it is passed to the JavaScript Engine (like V8).

  • The code is compiled and executed on the Main Thread.

  • Any other tasks (like responding to a user's click or rendering an animation) are queued and must wait until the script finishes running.

6 Resuming the Parser: Only after the script has finished executing does the browser signal the HTML parser to resume. It picks up exactly where it left off and continues building the rest of the DOM tree.

In case of async?

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

in that case, when script tag is discovered with async attribute, it still fetched the script but continue parsing HTML. Then once the script file is downloaded, the browser pause HTML parsing, execute the script file on the main thread. once the script file is executed fully the HTML parsing is resumed.

Key Characteristics of async

  • Order is not guaranteed: If you have three async scripts, the smallest one will likely finish downloading first and execute first, regardless of their order in your HTML.

  • Execution is blocking: While the download is non-blocking, the execution still stops the parser.

  • DOM Risk: Because async scripts run as soon as they download, they might execute before the element they are trying to manipulate even exists in the DOM.

When to use async?

async is perfect for independent scripts that don't care about the DOM or other scripts—like Google Analytics, Ads, or Tracking Pixels. If your script needs to wait for the whole page to be ready or depends on another library (like jQuery or a utility file), you should use defer instead.

in case of defer?

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

in that case, when script tag is discovered with defer attribute, it still fetched and download the script but continue parsing HTML. after parsing complete the script tag started executing.

Key Feature: If you have multiple defer scripts, they are guaranteed to execute in the exact order they appear in your HTML, regardless of which one finished downloading first.

What Actually Blocks the Main Thread?

While parsing HTML itself is usually very fast, certain things will cause the Main Thread to stop parsing entirely:

  • Synchronous Scripts: As we discussed, a <script> tag without async or defer stops the HTML parser. The Main Thread stops building the DOM until the script is downloaded and executed.

  • CSS and JavaScript Interaction: If the browser encounters a script while it is still downloading CSS, it will often block the script execution (and thus the HTML parsing) until the CSS is finished downloading and the CSSOM (CSS Object Model) is ready. This is because the script might ask the browser for the style of an element (e.g., getComputedStyle), and the browser needs the CSS to answer correctly.

If you have a large script at the top of your <head>, the user will see a blank white screen until that script is fetched and executed, because the browser is blocked from parsing the rest of the HTML (like your text and images).

To fix this, developers use async (to download in the background and run immediately) or defer (to download in the background and run only after the HTML is fully parsed).