Script Loading: async vs defer - When to Use Each

Easyβ€’

The real difference between regular, async, and defer scripts is not just download timing. It is whether the browser must stop parsing, whether execution order is guaranteed, and whether the script can safely wait until the document has been parsed.

Asked In

Quick Decision Guide

Use defer for most application scripts that need predictable order or depend on the parsed DOM.

Use async for independent scripts like analytics, ads, or widgets where execution order does not matter.

Use a plain external <script> without attributes only when parser-blocking behavior is truly required.

type="module" scripts are deferred by default, so they usually behave more like defer than classic blocking scripts.

Regular Script Loading (No Attribute)

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

How It Works

For a classic external script without async or defer:

1. HTML parsing stops

2. The browser fetches the script

3. The script executes immediately

4. HTML parsing resumes

Mental Model

> The parser hits the script tag and must wait.

Why It Matters

Parser-blocking scripts delay visible progress because the browser cannot continue parsing the document until that script is ready and executed.

When It Still Makes Sense

β€’tiny critical inline bootstrap code
β€’code that must run before later markup is parsed
β€’rare compatibility or sequencing cases where blocking is intentional

Example

<head>
  <script>
    window.APP_CONFIG = { apiBase: '/api' };
  </script>
</head>
<body>
  <script src="app.js"></script>
</body>

Trade-off

Blocking is simple and predictable, but usually the worst option for page startup performance.

The async Attribute

The `async` Attribute

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

How It Works

For a classic external script with async:

1. HTML parsing continues

2. The script downloads in parallel

3. As soon as the script is ready, it executes immediately

4. That execution may happen before parsing completes

Mental Model

> Download early, run whenever ready.

Key Characteristics

Advantages

β€’non-blocking download
β€’fast execution once the resource is ready
β€’good for scripts that do not depend on document order or the parsed DOM

Disadvantages

β€’execution order is not guaranteed between multiple async scripts
β€’can run before the DOM is fully parsed
β€’can interrupt parsing when it executes

Best Use Cases

β€’analytics
β€’ads
β€’third-party tags
β€’independent widgets
β€’scripts that do not depend on other scripts

Common Mistake

<!-- ❌ Order is not guaranteed -->
<script async src="jquery.js"></script>
<script async src="plugin.js"></script>

If plugin.js expects jQuery, this can break because plugin.js may execute first.

Better Alternative

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

Interview Takeaway

Use async when independence matters more than ordering.

The defer Attribute

The `defer` Attribute

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

How It Works

For a classic external script with defer:

1. HTML parsing continues

2. The script downloads in parallel

3. Execution waits until parsing finishes

4. Multiple deferred classic scripts execute in document order

Mental Model

> Download now, execute later, preserve order.

Key Characteristics

Advantages

β€’non-blocking download
β€’runs after parsing completes
β€’preserves execution order across deferred classic scripts
β€’avoids parser interruption during download phase
β€’strong default for application code

Disadvantages

β€’does not run as early as async
β€’not useful when immediate execution is required

Why It’s Often the Best Default

Application scripts usually need at least one of these:

β€’parsed DOM
β€’predictable order
β€’stable startup sequence

That makes defer a better default than async for most app bundles.

Example

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

These execute after parsing, in the same order they appear.

Important Detail

Deferred classic scripts run before DOMContentLoaded, which means that event waits for them to finish.

Execution Order and DOM Readiness

This is where interview answers usually get separated.

πŸ”₯ Insight

The real choice is often not β€œfast vs slow.” It is unpredictable early execution vs predictable late execution.

Comparison

Regular script

β€’blocks parsing
β€’executes immediately
β€’order follows document order

`async`

β€’does not block download
β€’executes as soon as ready
β€’order is not guaranteed
β€’may run before DOM is fully parsed

`defer`

β€’does not block download
β€’executes after parsing
β€’order is preserved
β€’good fit for DOM-dependent scripts

Rule of Thumb

β€’Need predictable order? β†’ defer
β€’Need DOM to be parsed first? β†’ defer
β€’Script is fully independent? β†’ async
β€’Need immediate parser-blocking behavior? β†’ plain script

Module Scripts

<script type="module" src="app.js"></script>

Module scripts are deferred by default.

What That Means

β€’they download in parallel with parsing
β€’they execute after parsing by default
β€’defer has no effect on them

Important Nuance

You can still mark a module script as async:

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

In that case, the module and its dependency graph are fetched in parallel and evaluated as soon as available.

Practical Takeaway

If you are using modern ES modules, you usually do not need to add defer manually.

Performance and Common Pitfalls

What Actually Improves Performance

β€’avoid parser-blocking scripts on the critical path
β€’use defer for main application bundles
β€’use async for truly independent third-party scripts
β€’reduce script size and count where possible

Common Pitfalls

1. Using `async` for dependent scripts

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

If app.js depends on vendor.js, this is fragile.

2. Blocking parsing with non-critical third-party code

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

This slows parsing for no good reason.

3. Assuming `async` waits for DOM

n

It does not. An async script can execute before the DOM is fully parsed.

4. Adding `defer` to module scripts expecting different behavior

Module scripts already defer by default.

Best Practices

Decision Tree

Does the script need predictable order or parsed DOM?
β”œβ”€ Yes β†’ use defer
└─ No β†’ is it independent?
   β”œβ”€ Yes β†’ use async
   └─ No β†’ use defer

Common Patterns

Pattern 1: Modern Web App

<body>
  <div id="root"></div>

  <script defer src="runtime.js"></script>
  <script defer src="vendor.js"></script>
  <script defer src="app.js"></script>
</body>

Pattern 2: App + Analytics

<body>
  <script async src="analytics.js"></script>
  <script defer src="app.js"></script>
</body>

Pattern 3: ES Modules

<body>
  <script type="module" src="app.js"></script>
</body>

Final Rule of Thumb

For most application code, defer is the safest default.

Use async only when the script is independent and order does not matter.