Async & Defer

By default, HTML is parsed from top to bottom.

When the browser encounters an <img> tag, it starts downloading the image but continues parsing the rest of the HTML.

However, when it encounters a <script> tag:

The browser pauses HTML parsing, downloads the JavaScript, executes it, and only then continues parsing the HTML.

This behavior is called blocking.

The async and defer attributes allow us to modify how scripts are loaded and executed.

Async

With async:

  • HTML parsing continues while the JavaScript file is downloading
  • As soon as the JavaScript finishes downloading, HTML parsing pauses during execution
  • The script executes immediately
  • Then HTML parsing resumes

Key idea:

Async scripts run as soon as they’re ready, not in order

This means:

  • execution order is not guaranteed and may differ from script order in the HTML
  • best for scripts that are independent (e.g. analytics)

Defer

With defer:

  • HTML parsing continues while the JavaScript file downloads
  • The script does not execute immediately after downloading
  • Instead, it waits until all HTML parsing is complete
  • Then scripts execute in the order they appear in the document

Key idea:

Defer scripts run after the DOM is fully built

Image depiction of HTML parsing and JS downloading and executing behavior depending on use of asyn and defer attributes or no attributes.

Why This Matters

Some developers choose to always use defer when loading scripts because:

  • scripts download early (better performance)
  • scripts run late (after the DOM exists)
  • execution order is preserved

This removes the need to place <script> tags at the bottom of the <body>. (See below for common use cases for async).

FeatureAsyncDefer
Blocks HTML parsingOnly during executionNever during parsing
Execution timingAs soon as readyAfter HTML parsing
Order guaranteed❌ No✅ Yes

One important constraint

defer only works for external scripts and is ignored on inline scripts:

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

Not:

<script defer>
  // inline JS
</script>

Good Use Cases for Async

Use async for scripts that are independent of your application logic and do not rely on execution order, such as analytics, ads, and third-party widgets.

Use async when the script:

  • does not depend on other scripts
  • does not need the DOM to be fully ready
  • can run whenever it finishes downloading

Examples of when to use Async

Analytics / Tracking

<script async src="https://www.googletagmanager.com/gtag/js"></script>

Why:

  • does not affect UI
  • does not depend on your app logic
  • order doesn’t matter

Ads / Ad networks

<script async src="https://ads.network.com/script.js"></script>

Why:

  • completely independent
  • performance matters (don’t block page load)
  • execution timing is not critical

Social media widgets

<script async src="https://platform.twitter.com/widgets.js"></script>

Why:

  • enhances the page, not required for core functionality
  • doesn’t need to run in a specific order

Feature scripts that don’t depend on the DOM

Example: logging, metrics, or background tasks

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

Why:

  • not tied to specific elements
  • can run anytime

Independent micro-features

Example: a script that enhances something optional like:

  • A/B testing
  • heatmaps
  • feedback tools
<script async src="/js/heatmap.js"></script>

Why:

  • does not need to coordinate with your main app
  • safe to run whenever

When NOT to use async

When order matters

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

Problem:

  • app.js might run before lib.js

When scripts depends on the DOM

document.querySelector(".btn")

If the DOM isn’t ready yet → null

When scripts depend on each other

Example:

// app.js depends on helper.js

Async can break this.

To decide, ask yourself:

“If this script runs early, late, or out of order… will anything break?”
  • Yes → use defer
  • No → async is safe

Use caseasyncdefer
Analytics
Ads
UI logic
DOM manipulation
Script dependencies