HTMLPerformanceMedium

async vs defer

01

The Short Answer

async and defer are attributes on <script> tags that control how external JavaScript files are loaded and executed relative to HTML parsing. Both allow the browser to download the script without blocking HTML parsing, but they differ in when the script executes. async executes as soon as the download finishes (potentially interrupting parsing), while defer waits until the HTML is fully parsed before executing. This distinction matters for page load performance and script dependency order.

02

Default Script Behavior (No Attribute)

Without async or defer, the browser stops parsing HTML when it encounters a <script> tag, downloads the script, executes it, and only then resumes parsing. This is called parser-blocking — it's the worst case for performance because the user sees a blank or incomplete page while scripts load.

default-script.htmlhtml
<!-- Parser-blocking: HTML parsing stops here until script downloads + executes -->
<script src="app.js"></script>

<!-- Timeline:
  1. Parse HTML... (stops at script tag)
  2. Download app.js (network wait)
  3. Execute app.js (CPU work)
  4. Resume parsing HTML
-->
03

async

async tells the browser to download the script in parallel with HTML parsing (non-blocking download). As soon as the download finishes, parsing pauses and the script executes immediately. The key implication: execution order is not guaranteed — whichever script finishes downloading first runs first, regardless of their order in the HTML.

async-script.htmlhtml
<script async src="analytics.js"></script>
<script async src="ads.js"></script>

<!-- Timeline:
  1. Parse HTML + download both scripts in parallel
  2. When analytics.js finishes downloadingpause parsingexecuteresume
  3. When ads.js finishes downloadingpause parsingexecuteresume
  (order depends on which downloads fasterNOT document order)
-->

Because execution order is unpredictable, async is only safe for scripts that are completely independent — they don't depend on other scripts and nothing depends on them. Analytics, ads, and tracking pixels are the classic use case.

04

defer

defer also downloads the script in parallel with parsing (non-blocking download), but it guarantees two things: the script won't execute until HTML parsing is complete, and multiple deferred scripts execute in document order. This makes it safe for scripts that depend on the DOM being ready or on each other.

defer-script.htmlhtml
<script defer src="vendor.js"></script>
<script defer src="app.js"></script>

<!-- Timeline:
  1. Parse HTML + download both scripts in parallel
  2. HTML parsing completes (DOMContentLoaded hasn't fired yet)
  3. Execute vendor.js (guaranteed firstdocument order preserved)
  4. Execute app.js (guaranteed second)
  5. DOMContentLoaded fires
-->

Deferred scripts execute just before the DOMContentLoaded event, after the full DOM is built. This means they can safely access any DOM element without worrying about whether it exists yet. The order guarantee means app.js can safely depend on vendor.js having already run.

05

Visual Comparison

No attributeasyncdefer
Blocks HTML parsing?Yes (download + execute)Only during executionNever
DownloadSequential (blocks)Parallel with parsingParallel with parsing
Execution timingImmediately when reachedAs soon as downloadedAfter HTML fully parsed
Execution orderDocument orderUnpredictable (download order)Document order guaranteed
DOM ready at execution?Only elements above the tagNot guaranteedYes — full DOM available
DOMContentLoadedWaits for scriptDoesn't waitFires after deferred scripts run
06

When to Use Each

  • Use defer for:
    • Your main application bundle (needs DOM, has dependencies)
    • Libraries that other scripts depend on (order matters)
    • Any script that accesses DOM elements
    • Scripts in <head> that should run after parsing
  • Use async for:
    • Analytics scripts (Google Analytics, Mixpanel)
    • Ad scripts that are self-contained
    • Third-party widgets that don't interact with your code
    • Any script that's completely independent
  • Use neither (or put at end of <body>) for:
    • Inline scripts that must run immediately
    • Scripts that use document.write (rare, legacy)
    • Critical render-blocking scripts (very rare)
07

Important Edge Cases

async and defer on inline scripts

Both async and defer only work on external scripts (with a src attribute). On inline <script> tags, these attributes are ignored — inline scripts always execute immediately and block parsing.

Module scripts are deferred by default

<script type="module"> behaves like defer automatically — it downloads in parallel and executes after parsing, in document order. Adding async to a module script makes it execute as soon as its dependencies are loaded, like async on regular scripts.

08

Why Interviewers Ask This

This question tests your understanding of how browsers load and execute resources, and how that impacts page performance. Interviewers want to see that you know the difference between blocking and non-blocking script loading, understand execution order guarantees, can choose the right attribute for different script types, and know that module scripts are deferred by default. It's a practical performance optimization that every frontend developer should know.

Quick Revision Cheat Sheet

async: Download parallel, execute ASAP — order not guaranteed

defer: Download parallel, execute after parsing — order guaranteed

Neither: Blocks parsing during download AND execution

async use case: Independent scripts: analytics, ads, tracking

defer use case: App code that needs DOM or depends on other scripts

Modules: type="module" is deferred by default