Suggest an editImprove this articleRefine the answer for “defer vs async in a script tag”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Both attributes let the script download in parallel with HTML parsing, but they differ in when it runs: `defer` executes the script after the HTML is fully parsed and strictly in declaration order, while `async` executes it as soon as it has downloaded, in an unpredictable order.** A plain `<script src="...">` with no attributes halts HTML parsing until the file has downloaded and run, so the user stares at a blank screen. `defer` waits for a complete DOM and delays the `DOMContentLoaded` event, which makes it right for the main application logic that depends on the markup. `async` waits for nothing and blocks nothing, which makes it right for independent third-party scripts such as analytics. Both attributes only apply to external scripts with a `src`. ```html <script src="main.js" defer></script> <script src="analytics.js" async></script> ``` **Key point:** `defer` means parallel download plus ordered execution after the DOM is built, `async` means parallel download plus execution the moment the file arrives.Shown above the full answer for quick recall.Answer (EN)Image**`defer` and `async` are `<script>` attributes that remove the blocking of HTML parsing: both download the file in parallel with the markup, but `defer` runs the scripts after the DOM is built and in declaration order, while `async` runs each script immediately after it downloads, in an arbitrary order.** That moment of execution is the entire difference between them. ## Theory ### TL;DR - With no attributes, `<script src>` halts HTML parsing, which blocks rendering. - `defer`: parallel download, execution after the HTML is fully parsed, order preserved. - `async`: parallel download, execution right after the download, order not guaranteed. - `defer` scripts run before `DOMContentLoaded` and the event waits for them; `async` does not. - `defer` is for the main site logic; `async` is for independent third-party scripts. - Both attributes are ignored on inline scripts that have no `src`. ### Quick example ```html <!-- HTML is being parsed... --> <script src="main.js" defer></script> <script src="analytics.js" async></script> ``` | Step | What happens | | --- | --- | | 1 | The browser starts parsing HTML | | 2 | It downloads both scripts at the same time | | 3 | `analytics.js` (async) downloads faster, so it runs immediately | | 4 | The HTML finishes parsing | | 5 | Then `main.js` (defer) runs | | 6 | Then `DOMContentLoaded` fires | ### The problem defer and async solve When the browser meets this tag: ```html <script src="script.js"></script> ``` by default it: 1. Stops parsing HTML; 2. Downloads `script.js`; 3. Executes it; 4. And only then carries on building the DOM. This blocks page rendering, so the user sees a blank screen until the JS has loaded. That is exactly why the `defer` and `async` attributes appeared: to avoid blocking HTML parsing. ### How defer works ```html <script src="script.js" defer></script> ``` 1. The script downloads in parallel with the HTML, asynchronously. 2. But it executes only after the HTML is fully parsed, that is when the DOM is ready, and before the `DOMContentLoaded` event. 3. Scripts with `defer` execute in the order in which they appear in the HTML. This is an excellent fit for the main application scripts, for example `main.js` or `vendor.js`. ```html <script src="a.js" defer></script> <script src="b.js" defer></script> ``` Both download in parallel first, but they execute strictly in sequence: `a.js` first, then `b.js`, and only once the DOM has been built. That makes `defer` safe for code that touches page elements straight away and for files that depend on one another. ### How async works ```html <script src="script.js" async></script> ``` 1. The script downloads in parallel with the HTML, just as with `defer`. 2. But it executes as soon as it has downloaded, even if the HTML is not fully parsed yet. 3. Scripts with `async` execute in an unpredictable order, depending on which one downloads first. It suits independent scripts, for example analytics or advertising. ```html <script src="a.js" async></script> <script src="b.js" async></script> ``` Here `b.js` may run before `a.js` if it downloaded faster, even though it sits lower in the HTML. On top of that, running an `async` script interrupts HTML parsing at the moment the file arrives, so a heavy `async` script can still slow rendering down. ### Comparison | Property | `defer` | `async` | | --- | --- | --- | | Script download | Asynchronous, in parallel with HTML | Asynchronous | | Execution | After the HTML is fully parsed | Immediately after the download | | Execution order | Preserved | Not guaranteed | | Blocks HTML parsing | No | Not while downloading, but yes while executing | | The `DOMContentLoaded` event | Waits for all `defer` scripts | Does not wait | | Typical scenarios | The main site logic | External trackers, advertising | ### What to choose in practice | Option | When to use it | | --- | --- | | `defer` | For the main JS code that depends on the DOM, for example interface initialisation | | `async` | For independent scripts: analytics, metrics, advertising | | No attribute | Only when you genuinely need to block HTML parsing, which is rare | A tip: if you use a modern bundler (Next.js, Vite, Webpack), it adds `defer` to the `<script>` in `head` for you, so in modern applications this is already the default behaviour. It is also worth remembering that `<script type="module">` behaves like `defer` automatically, and to get `async` behaviour for a module you have to state the attribute explicitly. ### Common mistakes - **Putting `defer` or `async` on an inline script.** Without a `src` both attributes are ignored and the script runs synchronously in place. - **Relying on order with `async`.** If `b.js` needs variables from `a.js`, `async` breaks that randomly and only on a slow network. - **Using `async` for code that works with the DOM.** The script can run before the element it needs exists in the document, and `querySelector` returns `null`. - **Confusing "does not block parsing" with "does not block at all".** An `async` script still occupies the main thread while it executes. - **Expecting `DOMContentLoaded` to wait for `async`.** The event can fire before such a script, so a listener registered inside an `async` file sometimes simply arrives too late. - **Leaving `document.write()` in a deferred script.** After the document has been parsed it wipes the page, so it must not be used with `async` or `defer`.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.