# How to Handle JavaScript Non-Blocking Loading with `async` and `defer`

> Learn to handle JavaScript non-blocking loading using async and defer attributes. Optimize your site performance by downloading scripts in parallel without blocking the HTML parser.

- Repository: [David Dias/Front-End-Checklist](https://github.com/thedaviddias/Front-End-Checklist)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Use the `async` attribute for independent scripts that don't touch the DOM and the `defer` attribute for scripts that require the full document structure, ensuring both download in parallel without blocking the HTML parser.**

By default, browsers encounter a **render-blocking** behavior when loading JavaScript: the parser stops building the DOM, fetches the script, executes it, and only then resumes parsing. This process delays First Contentful Paint, particularly on mobile networks. The **Front-End Checklist** repository by `thedaviddias` explicitly recommends loading JavaScript asynchronously using these attributes to improve page performance metrics.

## Understanding Render-Blocking Behavior

When the browser parser hits a standard `<script>` tag without any attributes, it immediately pauses HTML parsing to fetch and execute the code. This synchronous loading increases time-to-interactive and can degrade user experience on slow connections. Modern HTML specifications provide `async` and `defer` to eliminate this bottleneck, allowing scripts to download concurrently while the document continues parsing.

## Using the `async` Attribute

The `async` attribute downloads the script file in parallel with HTML parsing and executes it **immediately after the download completes**, even if the parser is still processing the document.

This approach suits small, self-contained scripts such as analytics trackers or feature-detection utilities that do not depend on DOM elements or other scripts. Because execution order is not guaranteed, avoid `async` for scripts with dependencies.

```html
<script src="https://example.com/analytics.js" async></script>

```

**Important:** The `async` attribute requires an external script with a `src` attribute and cannot be used on inline scripts.

## Using the `defer` Attribute

The `defer` attribute also downloads the script file in parallel with parsing, but it delays execution until **after the entire document has been parsed**, just before the `DOMContentLoaded` event fires.

This method preserves the execution order of multiple deferred scripts and guarantees the DOM is fully constructed when the script runs. Use `defer` for main application bundles, frameworks, or any code that queries or manipulates DOM elements.

```html
<script src="js/app.bundle.js" defer></script>

```

## Comparing `async` vs `defer`

| Attribute | Download Timing | Execution Timing | Best For |
|-----------|----------------|------------------|----------|
| `async` | Parallel with parsing | Immediately after download (pauses parser) | Independent scripts (analytics, ads) |
| `defer` | Parallel with parsing | After document parse, before `DOMContentLoaded` | DOM-dependent scripts, ordered execution |

Scripts marked with `defer` maintain their relative execution sequence, while `async` scripts execute as soon as they arrive, potentially out of order.

## Practical Implementation Strategies

For applications using multiple scripts with different loading requirements, mix strategies carefully. Place independent utilities with `async` and core application code with `defer` to ensure dependencies resolve correctly.

```html
<!-- Independent utility library (doesn't touch the DOM) -->
<script src="js/utility.js" async></script>

<!-- Core framework that must execute after the DOM is built -->
<script src="js/framework.js" defer></script>

<!-- Application code that depends on the framework -->
<script src="js/main.js" defer></script>

```

For legacy browser support (very old browsers that do not recognize these attributes), dynamically inject scripts as a fallback:

```javascript
var script = document.createElement('script');
script.src = 'js/app.bundle.js';
script.defer = true;
document.head.appendChild(script);

```

## Front-End Checklist Recommendation

According to the source code in [`README.md`](https://github.com/thedaviddias/Front-End-Checklist/blob/main/README.md) at line 532, the Front-End Checklist identifies "JavaScript files are loaded asynchronously using `async` or deferred using `defer`" as a medium-priority performance item. Following this guideline reduces render-blocking resources, improves First Contentful Paint scores, and aligns with modern web performance best practices.

## Summary

- **Render-blocking** occurs when standard scripts pause HTML parsing during download and execution.
- Use **`async`** for external scripts that don't require the DOM or specific execution order.
- Use **`defer`** for external scripts that need the full DOM or must execute in sequence.
- Both attributes require the `src` attribute and do not work on inline scripts.
- The `thedaviddias/Front-End-Checklist` explicitly recommends these attributes at [`README.md`](https://github.com/thedaviddias/Front-End-Checklist/blob/main/README.md) line 532 for optimal loading performance.

## Frequently Asked Questions

### Can I use `async` or `defer` on inline scripts?

No. Both attributes are only valid on external scripts that include a `src` attribute. Inline scripts without a `src` cannot be loaded asynchronously or deferred using these HTML attributes.

### When should I choose `defer` over `async`?

Choose **`defer`** when your script needs to access DOM elements, relies on other scripts loading first, or requires predictable execution order. Choose **`async`** for standalone scripts like analytics or tracking pixels that execute independently of the page structure.

### What happens to deferred scripts when the document finishes parsing?

Deferred scripts execute **after the HTML parser completes** but before the `DOMContentLoaded` event fires. This timing ensures the full DOM is available while still allowing the script to run before sub-resource loading completes.

### How do I handle browsers that don't support `async` or `defer`?

For very old browsers that lack support, dynamically create script elements using JavaScript and set the `defer` or `async` properties before appending them to the document head. This method provides equivalent non-blocking behavior in modern browsers while ensuring functionality in legacy environments.