Skip to main content
Knowledge Hub

Script Defer and Async

Understanding how scripts load and execute in the browser

Last updated: February 25, 2025

Modern browsers can load JavaScript in several ways. The attributes defer and async both allow scripts to download without blocking HTML parsing, but they differ in when the script executes.

Understanding these loading strategies helps you optimize page load performance and ensure scripts execute in the right order.

Default Behavior (No Attributes)

When you use a normal script tag:

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

The browser:

  1. Stops parsing HTML when it reaches this line
  2. Downloads main.js
  3. Executes it immediately
  4. Then resumes HTML parsing

This blocks rendering, delaying page load and interactivity.

Non-Blocking Attributes

AttributeBlocks HTML Parsing?Execution TimingKeeps Script Order?
(none)YesImmediately when encounteredYes
asyncNoAs soon as downloadedNo
deferNoAfter HTML parsing completesYes

How defer Works

When you write:

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

The browser changes when and how the script loads and runs.

Step-by-Step

  1. HTML parsing begins: The browser starts building the DOM, reading HTML line by line.

  2. Deferred script encountered: When the browser sees <script src="main.js" defer>, it:

    • Starts downloading the file asynchronously. The browser’s resource loader and network layer handle this fetch while parsing continues.
    • Continues parsing HTML without interruption.
  3. Download completes: Once main.js is fully downloaded, it’s stored in memory, ready for later execution.

  4. DOM parsing finishes: The script runs only after the entire HTML document is parsed.

  5. Deferred scripts execute: Scripts execute in the same order they appear.

  6. DOMContentLoaded fires: The browser fires the DOMContentLoaded event after all deferred scripts have finished.

Technical Flow

HTML Parser in Browser

   ├─► Encounters <script defer>
   │       └─► Adds to Resource Loader queue

   ├─► Continues parsing HTML (non-blocking)

Network Layer
   ├─► Checks HTTP cache
   ├─► Sends HTTP GET request to server
   ├─► Streams response into memory
   └─► Marks script ready (notify parser)

DOM Ready
   ├─► Executes deferred scripts in order
   └─► Fires DOMContentLoaded event

How async Works

When you write:

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

The browser downloads the script without blocking, but executes it as soon as it’s ready, even if HTML parsing isn’t complete.

Step-by-Step

  1. HTML parsing begins: The browser starts building the DOM.

  2. Async script encountered: The browser starts downloading the file asynchronously and continues parsing HTML.

  3. Download completes: Pauses HTML parsing temporarily, executes the script immediately, and then parsing continues where it left off.

Why the Network Layer Is Used

Browsers have layered subsystems:

LayerRole
HTML ParserReads and builds the DOM
Network / Resource LoaderFetches resources (scripts, CSS, images)
JavaScript Engine (e.g., V8)Executes JS code
RendererPaints the page visually

The JavaScript engine doesn’t fetch files, it only runs them. The network layer handles all I/O (HTTP, TCP, caching, etc.).

Example request:

GET /main.js HTTP/1.1
Host: example.com

Example response:

HTTP/1.1 200 OK
Content-Type: application/javascript

Analogy

Think of the browser as a restaurant kitchen:

RoleComponentDescription
ChefHTML ParserReads the recipe (HTML) and builds the DOM
Delivery PersonNetwork LayerFetches ingredients (JS files) in parallel
CookJS EngineExecutes scripts once everything’s ready
  • Without defer: The chef stops cooking every time an ingredient is missing
  • With defer: The chef keeps working while the delivery person fetches everything; cooking starts after all prep is done
  • With async: The delivery person brings ingredients as they arrive, interrupting the chef to use them immediately

Why Use defer

  • Improved performance: HTML keeps loading while JavaScript downloads
  • Guaranteed order: Scripts run in appearance order
  • DOM ready: Ensures the DOM is built before running
  • Best for initialization or DOM-manipulation scripts

When to Use async

Use for independent scripts, ones that don’t depend on the DOM or other scripts.

Common examples:

  • Analytics (analytics.js)
  • Ads or tracking tags
  • External libraries that don’t modify the DOM
<script src="https://example.com/tracker.js" async></script>

Analogy for async

Think of async as a worker who rushes in to perform a task the moment they’re ready, even if the rest of the team (HTML parser) is still working.

  • Async: “Run as soon as ready, ignore others”
  • Defer: “Wait until everyone (DOM) is ready, then run in order”

Key Notes

  • defer and async only work with external scripts (src required)
  • type=“module” scripts behave like defer automatically
  • Even though both load files asynchronously, when executed, they still run on the main thread, not in the background
  • If both async and defer are present, async takes precedence

Summary Table

AttributeHTML ParsingScript DownloadScript ExecutionExecution OrderUse Case
(none)BlockedSequentialImmediatePreservedCritical inline logic
asyncNon-blockingParallelAs soon as readyUnorderedAnalytics, ads
deferNon-blockingParallelAfter DOM parseOrderedDOM-related scripts

Understanding script loading strategies is essential for optimizing page load performance. Using defer and async appropriately can significantly improve perceived performance and user experience.