Skip to content

Async and defer inside the script tags

JavaScriptAsyncDeferCover

In the last article, in case you missed it, we’ve written about the procedure of parsing the HTML inside the browser. I mentioned there that the browser will stop parsing the document if it comes across <script>...</script> or <script src=" ">...</script> tags. The browser stops parsing the page and starts executing the script immediately (if the script is external). Then the browser will download the script, execute the script, and only then continue the initial work.

This poses a question. What if for some reason, we don’t have access to a particularly fast internet connection, and if we try to download a huge HTML file with a lot of nested script objects? This can have a huge impact on the loading time of our webpage. The page itself would be blank and the user experience would be forever ruined. Ok, maybe not, but it would definitely be quite annoying! :D.

Put your script tags at the bottom

The first lesson to learn is to never put script tags inside the <head> elements.

<!doctype html>defer
<html>
<head>
    <title>Example Domain</title>
    <script src="script.js"></script>
    <style type="text/css">
    </style>    
</head>

<body>
<div>
   ...
</div>
</body>
</html>

The page would be blocked for a long time! Additionally, by blocking the parsing of the HTML, the script can’t interact with the DOM!).

So we put our <script>...</script> tags right before we close our body tag, as shown in the following listing:

<!doctype html>defer
<html>
<head>
    <title>Example Domain</title>
    <style type="text/css">
    </style>    
</head>

<body>
<div>
   ...
</div>
<script src="script.js"></script>
</body>
</html>

Async And Defer

Defer

But if we use newer browsers (and almost all modern browsers have async and defer included as part of the browser’s documentation), we can asynchronously retrieve the accompanying JavaScript logic, and let the browser parse HTML without any interruptions!

First thing first, for async and defer to actually work we need to put our <script> tag inside the <head> tag. So, we actually go back to the things I’ve said not to do 🙂

Using defer we tell the browser to continue parsing the HTML, and let the script download in the background. The script will run once it’s fully downloaded:

<!doctype html>defer
<html>
<head>
    <title>Example Domain</title>
    <script defer src="script.js"></script>
    <style type="text/css">
    </style>    
</head>

<body>
<div>
   ...
</div>
</body>
</html>

Deferred scripts will never block the parsing of the HTML, and will execute once the DOM is ready (it will wait for the DOMContentLoaded event, that is – when the HTML document has been completely loaded and parsed). A thing to note regarding deferred scripts is that they keep their relative order, that is:

...
<script defer src="https://somesource.com/script1.js"></script>
<script defer src="https://somesource.com/script2.js"></script>
<script defer src="https://somesource.com/script3.js"></script>
...

The browser will actually download the scripts in parallel, but it will wait until all the scripts are downloaded and will only then run all the scrips in the order they are written, the script1.js first, then the script2.js and finally the script3.js.

Async

Async is also non-blocking, but it differs in a couple of important points. If a script has the async attribute, it doesn’t wait for other scripts (for comparison, defer waits), and the async script will load immediately when ready, not caring for anything else (even the DOMContentLoaded event).

...
<script async src="https://somesource.com/script1.js"></script>
<script async src="https://somesource.com/script2.js"></script>
<script async src="https://somesource.com/script3.js"></script>
...

Whichever one of those three scripts loads first the browser will run, and they don’t care whether the document has been fully downloaded.

JavaScript Async Defer Image
Different variations of including a script shown visually

Final Words

Because neither async nor defer block the initialization of the webpage, the webpage might be incomplete or still building. In this case, we should (in order to follow good UX practice) always help the user by showing a loader or any other indicator that the page is still not fully interactable.

For more articles please click below, or check the blog.