Boost Page Speed with async and defer
Tags: javascript, webdev, frontend, programming
Published: 2023-07-08 11:46:57

JavaScript is an essential component in web programming, enabling interactivity and dynamic behaviour on web pages. While HTML and CSS handle the structure and aesthetics, JavaScript brings the page to life.

There are different ways to add JavaScript code to an HTML file. The most common method is to store the JavaScript code in a separate file with a .js extension. Then, you can load that file into the HTML using the script tag. This allows the browser to download and execute the JavaScript code.

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Your Page Title</title>
    <script src="your-script.js"></script>
</head>
<body>
    <!-- Content of your webpage goes here -->
</body>
</html>
```


## The issue with loading a JavaScript file in the `<head>`

When a browser encounters a **`<script src='...'>...</script>`** tag in HTML, it pauses building the webpage and immediately executes the script. The browser must wait for the script to download, execute it, and only then can it continue processing the rest of the page.

![Rendering without any attributes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgcwdim2ujl39pq6j3pm.png)

This leads to 2 significant problems:

1. Scripts cannot interact with or modify elements that appear after them in the HTML code. This means they can't add event handlers or make changes to elements lower down the page.
2. If there is a large script at the top of the page, it causes a delay in displaying the page content. Users have to wait for the script to download and run before they can see the actual content of the page. 


## The Dirty Workaround:

So how do we fix this problem? One obvious but not-so-good fix is to move the script tag to the end of the `<body>` tag

```html
<body>
  ...all content is above the script...

  <script src="your-script.js"></script>
</body>
```

However, this solution is not without its drawbacks. One of the issues is that the browser detects and begins downloading the script only after it has finished downloading the entire HTML document. This can result in noticeable delays, particularly for long HTML documents.

While this delay may go unnoticed by users with fast internet connections, it poses a problem for individuals with slower internet speeds or unreliable mobile connections.

Thankfully, there are two attributes available for the **`<script>`** tag that provide solutions to this problem: `defer` and `async`.

## Async Attribute

> The `async` attribute is ignored if the `<script>` tag has no `src`.


```html
<script async src="your-script.js"></script>
```

The `async` attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script downloads “in the background”, and execute once done.


![Rendering with Async attribute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bclnwa3mhqi7t7akrp7.png)

### Important Considerations When Using the `async` Attribute

1) The async scripts execute in the `load-first` order:

It does not guarantee the order of execution of the script. For instance, a smaller script `small.js` goes second but probably downloads before `long.js`, so `small.js` executes first.

```html
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Your Page Title</title>
	<script async src="long.js"></script>
	<script async src="small.js"></script>
</head>
<body>
<!-- Content of your webpage goes here -->
</body>
</html>
```

2) `DOMContentLoaded` event and async scripts don’t wait for each other:

When it comes to the order of execution, the DOMContentLoaded event and async scripts are independent of each other. The DOMContentLoaded event may occur before or after an async script, depending on factors such as the script's loading time and whether it is already present in the HTTP cache.

```html
<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script async src="your-script.js"></script>
```

## Defer Attribute

> The `defer` attribute is ignored if the `<script>` tag has no `src`.


```html
<script defer src="your-script.js"></script>
```

The `defer` attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, and build DOM. The script loads in the background, and then runs when the DOM is fully built.


![Rendering with Defer attribute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wf09uvua25j9f27bogx4.png)

### Important Considerations When Using the `defer` Attribute

1) `defer` guarantees the order of execution of scripts just like regular scripts.

It guarantees the order of execution of the script. For instance, To enhance performance, browsers download scripts in parallel therefore smaller script `small.js` goes second but probably downloads before `long.js`, but it still waits and runs after `long.js` executes..

```html
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Your Page Title</title>
	<script defer src="long.js"></script>
	<script defer src="small.js"></script>
</head>
<body>
<!-- Content of your webpage goes here -->
</body>
</html>
```

2)  `DOMContentLoaded` event handler waits for the deferred script. It only triggers when the script is downloaded and executed.

```html
<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script defer src="your-script.js"></script>
```

## When should I use what?

1) If the script is modular and does not depend on any other scripts such as independent third-party scripts like counters, and ads then use `async`.

```html
<!-- Google Analytics is usually added like this -->
<script async src="https://google-analytics.com/analytics.js"></script>
```

2) If the script relies upon or is relied upon by another script then use `defer`.

3) If the script is small and is relied upon by an async script then use an inline script with no attributes placed above the `async` scripts.

## Dynamic Scripts

There is another important method of adding a script to a webpage. We can create a script element using JavaScript and add it to the document dynamically. Here's an example:

```js
let script = document.createElement('script');
script.src = "your-script.js";
document.body.append(script);
```

Once the script is appended to the document, it starts loading immediately.

It's important to note that dynamic scripts behave as `async` by default. This means that they will load asynchronously, potentially impacting the order of execution and interaction with other elements on the page.

To make it non-async, we can set 

```js
script.async = false
```

## What if we put both `async` and `defer` attributes in the same script tag?

If you specify both, `async` takes precedence on modern browsers, while older browsers that support `defer` but not `async` will fallback to `defer`.