← Concepts

阅读中文版本

Script Loading: async and defer

async and defer keep HTML parsing alive while scripts download: defer waits for parse completion and runs in document order, async runs whenever ready.

browser

Definition

Classic scripts without attributes block the HTML parser at their position until fetched and executed. defer downloads in parallel and executes in document order after parsing completes but before DOMContentLoaded; async downloads in parallel and executes as soon as ready, in arbitrary order. Module scripts default to deferred behavior.

Context and scope

Chosen when adding analytics, third-party widgets, and application bundles: defer for code that depends on DOM or script order, async for independent snippets like trackers.

Common misconceptions

  • async and defer are interchangeable — they differ precisely in execution ordering and DOM availability guarantees
  • defer waits for images — it waits only for parsing; DOMContentLoaded fires before images finish loading
  • Inline scripts can be deferred — the attributes affect fetching, so they mean nothing without a src

Anti-patterns

  • Loading an ordered bundle of scripts with async and shipping a race on execution order to production
  • Putting DOM-dependent scripts at the top without defer and blocking the parser for the whole download
  • Assuming defer scripts run after DOMContentLoaded listeners instead of before them

Related concepts

Related examples

Authoritative source

https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script

What it is

A classic script tag without attributes is a parser stop sign: HTML construction halts until the code is fetched and runs. Both attributes remove the stop sign during download; they differ at execution time. defer preserves document order and the parsed DOM; async forfeits both for earliest possible execution.

How it works

defer scripts execute after the document is parsed, in order, before DOMContentLoaded fires. async scripts execute immediately upon download completion, in whatever order that happens, and may run before or after DOMContentLoaded. Module scripts and their dependency graph behave like defer by default, with dynamic import choosing async semantics.

When to use them

Application bundles and any script touching the DOM get defer. Independent trackers and beacons get async. Ordered third-party snippets that document a load sequence need defer, since async silently reorders under varying network conditions.

Pitfalls

async order races surface only under slow first or fast later connections — test with throttled profiles. Deferred code still blocks DOMContentLoaded, so heavy startup work delays interactive state regardless of attribute. The fetchpriority and preload interplay changes download priority but never execution semantics.