defer and async change when an external <script> downloads and executes relative to HTML parsing. Both let the script download in parallel without blocking the parser — the difference is execution timing.
html
defer and async change when an external <script> downloads and executes relative to HTML parsing. Both let the script download in parallel without blocking the parser — the difference is execution timing.
Normal: parse──stop──[download+exec]──parse──────── (slowest)
async: parse──────────────────exec──parse──────── (runs whenever it arrives)
defer: parse───────────────────────────parse──exec (after parsing, before DOMContentLoaded)
async | defer | |
|---|---|---|
| Download | parallel | parallel |
| Executes | as soon as downloaded | after HTML fully parsed |
| Order preserved? | No (whoever finishes first) | Yes (document order) |
| Blocks parser? | only briefly during exec | never |
defer — for scripts that touch the DOM or depend on each other/order (your app code). They run after the DOM exists, in sequence, just before DOMContentLoaded.async — for independent third-party scripts that don't depend on the DOM or other scripts (analytics, ads). Order doesn't matter, run them ASAP.<script src="analytics.js" async></script> <!-- independent → async -->
<script src="app.js" defer></script> <!-- app logic, ordered → defer -->
Note: both only apply to external scripts (src); they're ignored on inline scripts. ES modules (type="module") are deferred by default.
Using defer/async keeps scripts from blocking HTML parsing, which directly improves load performance and First Contentful Paint.
Picking the right one (defer for ordered DOM-dependent app code, async for fire-and-forget third-party scripts) avoids both slow loads and order-dependency bugs.