Script Loading: async vs defer - When to Use Each
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.
Quick Navigation: Regular Script Loading (No Attribute) β’ The async Attribute β’ The defer Attribute β’ Execution Order and DOM Readiness β’ Module Scripts β’ Performance and Common Pitfalls β’ Best Practices
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
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
Disadvantages
Best Use Cases
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
Disadvantages
asyncWhy Itβs Often the Best Default
Application scripts usually need at least one of these:
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
`async`
`defer`
Rule of Thumb
deferdeferasyncModule Scripts
<script type="module" src="app.js"></script>Module scripts are deferred by default.
What That Means
defer has no effect on themImportant 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
defer for main application bundlesasync for truly independent third-party scriptsCommon 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 deferCommon 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.