When your scripts are heavy and/or you have a slow server, they can affect the performance of your page. To help with this, we have 2 attributes that we can add to our scripts so that they don’t block or delay the execution of our website.
But first we have to understand how the execution of a web works.
How the execution of a web works
When the browser loads HTML and finds a `script` tag, it cannot continue to build the DOM. It has to wait for the script to download and then, execute the downloaded script. Then it can process the rest of the page.
<p> First paragraph </p><script src=”XXX”><p> Second paragraph </p>
In this example, the browser is going to paint the “first paragraph”, then download and execute the script, and then paint the “second paragraph”.
To solve this problem (not block content display) you can put the script tag at the end of the body. But if your HTML is too large, it’s going to take a long time and delay the loading of the page.
To see it more clearly:
Scripts in head:
Scripts at the end of the body:
And at this point, defer and async start.
`defer` downloads the script during HTML parsing and then, when the parser is completed, the browser executes the script. So scripts with `defer` don’t block the render of a page.
An important thing is that `defer` scripts are executed when the DOM is ready, but before the DOMContentLoaded event. The DOMContentLoaded event fires when the script is downloaded and executed. A positive effect of this attribute is that the DOM will be available for your script.
Another characteristic of `defer` is that these scripts guarantee the execution in the order that they appear in the document. So you have to use `defer` if you need to keep the order. This makes defer the attribute of choice when a script depends on another script.
With `async`, the file gets downloaded asynchronously and then executed as soon as it’s downloaded, without respecting any order, so it should be used for totally independent scripts.
The `async` attribute doesn’t wait for the DOMContentLoaded event either.
In other words, `async` scripts load in the background and run when ready. And also, like the `defer` attribute, the `async` attribute doesn’t block any content.
A good example to use this attribute is with Google Analytics scripts.
To sum up
📣 Important: both attributes don’t have any effect on inline scripts.
When you want to use `defer` and `async` you must keep in mind that not all browsers accept them. You can check better in:
It’s important to highlight that IE9 and below have problems with `defer` and this browser doesn’t guarantee the execution order.
You can add `defer` and `async` together. If you add both, `async` overrides `defer` if the browser supports it. But you keep `defer` for old browsers that only accept `defer`.
Extra: Dynamic Scripts
Another and very used option to load a script asynchronously is to inject the `<script>` tag dynamically with JS:
const script = document.createElement(“script”);script.src = “/path-to/script.js”;document.getElementsByTagName(“head”).appendChild(script);
The scripts that are dynamically added to the document are asynchronous by default and they don’t block the render of the page either. Also, they are executed as soon as they have been downloaded. In other words: the same behavior as with the `async` attribute.
Also, the DOMContentLoaded event doesn’t wait for the script to run.
In some cases we can see that attribute `async` is added in these dynamic scripts, like this:
script.async = true;
And it is for Firefox 3.6 and Opera browsers, which don’t have the asynchronous behavior by default for injected scripts, but they support the `async` attribute.