Asynchronous Loading: Definition, Async vs Defer, Performance
Async loading lets resources download without blocking the page — async/defer scripts, lazy images, dynamic imports. Critical for fast LCP + low TBT.
What is asynchronous loading?
Asynchronous loading is the practice of fetching resources (scripts, stylesheets, images, code modules) without blocking the page's parsing, rendering, or main thread. Done right, async loading can shave seconds off page load — done wrong, it causes layout shifts, race conditions, and worse UX than synchronous loading.
The browser's parser is single-threaded by default: a synchronous <script> tag halts HTML parsing until the script downloads + executes. Async loading techniques let parsing continue in parallel.
Async loading techniques
| Technique | What it does | Best for |
|---|---|---|
<script async> | Download in parallel; execute when ready (out of order) | Independent third-party scripts |
<script defer> | Download in parallel; execute after parsing in document order | App scripts that need DOM ready |
<script type="module"> | Defer by default; ESM imports | Modern app code |
<img loading="lazy"> | Defer image load until near viewport | Below-the-fold images |
Dynamic import() | Load JS module on demand | Code splitting, route-based loading |
requestIdleCallback | Run code during browser idle time | Non-critical work (analytics, prefetch) |
| Web Workers | Background-thread JS execution | Heavy compute without UI freeze |
fetch() + Promises | Async HTTP requests | Data loading after initial render |
async vs defer: the critical distinction
<!-- Sync (default): blocks parsing -->
<script src="app.js"></script>
<!-- async: download in parallel, execute IMMEDIATELY when ready -->
<script async src="analytics.js"></script>
<!-- defer: download in parallel, execute AFTER parsing in order -->
<script defer src="app.js"></script>| Aspect | async | defer |
|---|---|---|
| Order of execution | Whichever loads first | Document order |
| Executes when | As soon as downloaded | After HTML parsed |
| Blocks parser? | No | No |
| DOM ready? | Maybe not | Yes (always) |
| Best for | Independent scripts (analytics, ads) | App code that needs DOM |
Rule of thumb: Use defer for your code, async for third-party scripts that don't depend on anything.
Image lazy-loading
<!-- Native lazy-load: defer until near viewport -->
<img src="hero.jpg" loading="eager" alt="Hero" width="1200" height="600">
<img src="footer-banner.jpg" loading="lazy" alt="Banner" width="800" height="200">
<!-- iframes too -->
<iframe src="https://youtube.com/..." loading="lazy"></iframe>Native lazy-loading is supported in all modern browsers. loading="eager" is the default; loading="lazy" defers.
Dynamic imports for code splitting
// Static import: bundled into main JS
import { heavy } from './heavy-module';
// Dynamic import: loaded on demand
button.addEventListener('click', async () => {
const { openModal } = await import('./modal');
openModal();
});
// Route-based: load each route only when navigated to
const Settings = lazy(() => import('./Settings'));Async data loading patterns
// Fetch + render — old style
fetch('/api/users')
.then(r => r.json())
.then(users => render(users));
// Modern: async/await
async function loadUsers() {
const res = await fetch('/api/users');
const users = await res.json();
render(users);
}
// Concurrent fetches
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);Async loading best practices
- Use defer for app scripts. Don't block parsing; run after DOM ready.
- Use async for analytics + ads. Independent scripts; don't care about order.
- Lazy-load below-the-fold images.
loading="lazy". - Eager-load LCP image.
fetchpriority="high"+ preload. - Code-split routes. Dynamic imports per route.
- Use Promise.all for concurrent fetches. Don't wait sequentially.
- Show loading states. Skeletons, spinners during async work.
- Handle errors. Every fetch can fail; user-friendly error UI.
- Throttle/debounce expensive async ops. Especially in scroll/input handlers.
Common pitfalls
- async on app code that needs DOM. Script runs before DOM ready; fails or weird behavior.
- Lazy-load above-the-fold image. Hurts LCP.
- Sequential awaits when parallel possible. Doubles loading time.
- Forgetting error handling on async. Unhandled promise rejection in console.
- No loading state. Page looks broken during async fetch.
- Race conditions. Two async ops update same state; whichever finishes last wins (often wrong).
- Memory leaks from uncancelled async. Component unmounts but async still resolves; updates dead state.
- Blocking await in critical path. Awaiting non-essential data delays render.
FAQ: asynchronous loading
async vs defer: which to use?
defer for your code (executes after DOM ready, in order). async for independent third-party (analytics, ads).
Does lazy-loading work on all browsers?
Native loading="lazy": ~95%+ of browsers (Safari 15.4+, Chrome 77+, Firefox 75+). For older browsers, IntersectionObserver polyfill.
How does dynamic import affect bundle size?
Dramatically — code is split into separate chunks loaded on-demand. Initial bundle drops; subsequent navigation slightly slower (network fetch).
Should I lazy-load all images?
No — lazy-load only below-the-fold. Above-the-fold (especially LCP) should be eager + high priority.
What's a race condition in async code?
Two async operations finish in unexpected order. Common: user types in search box, two requests fire, slower one returns last + overwrites correct results.
How do I cancel an async fetch?
AbortController: const ctrl = new AbortController(); fetch(url, { signal: ctrl.signal }); ctrl.abort();
What's the difference between Promise and async/await?
Same thing. async/await is syntactic sugar over Promises — easier to read, especially with sequences.
Test async-loading impact with LoadFocus
LoadFocus runs Lighthouse audits from 25+ regions, measuring LCP/INP/TBT — directly affected by async loading correctness. Sign up free at loadfocus.com/signup.
Related LoadFocus Tools
Put this concept into practice with LoadFocus — the same platform that powers everything you just read about.