DEV Community

Cover image for Async VS Defer - Understand The JavaScript Execution
Ranjeet Singh
Ranjeet Singh

Posted on • Originally published at jeetsdev.hashnode.dev

Async VS Defer - Understand The JavaScript Execution

As a web developer, how many times you have written this line in your code?

<script src="script.js"></script>
Enter fullscreen mode Exit fullscreen mode

Probably too many times to count, but do you really understand how the browser handles this simple line of code? We have always been advised to put our <script> tag at the end of the <body> tag, but again why is that so...? Is there any other place where we can put our <script> tag? What if we put the <script> tag inside the <head></head> tag of our HTML document like -

<!-- inside the head tag -->
<head>
    ...
    <title> .... </title>
    <script src="script.js"></script>
</head>
Enter fullscreen mode Exit fullscreen mode

Gif here
Aahhh, enough of these questions. Now it's time to understand all of them.

First and foremost, Yes we can put our <script> tag wherever we want, but remember one thing that it can affect your page performance.

So now let's understand exactly how <script> tag loading works and most importantly how we can use async and defer to speed up our Javascript loading and improve our page performance.

How Browser Parse HTML

Before understanding how <script> is loading, we first need to understand how the browser parses HTML. Luckily, it's pretty straightforward. The browser will parse HTML from the top of the document to the bottom, and when it hits a resource, like an <img> tag it will send out a request for that resource and continue parsing. The important thing to note is that the browser does not stop parsing the HTML to get the img src. This is why when you load a web page you may notice the page jumps around as the images pop in since they are loaded in the background and may finish downloading after the HTML is parsed.

But that's not the case with the <script> tag. When the browser comes across a <script> tag when loading HTML, the browser is forced to download and parse the entire <script> and evaluate it first, before it can continue with reading the rest of the HTML to build the DOM. This is why we are advised to put our <script> tag at the bottom of our HTML body so they don't delay the parsing of the HTML.

Now you can just think that putting the <script> tag at the bottom of the HTML body is ideal, but what if the HTML is too large and it takes some time to get downloaded and parsed then the JavaScript will not start downloading until all of the HTML is parsed which could delay your <script> download and affect your page performance. This is why the async and defer attributes were created.

Async and Defer

Both async and defer attributes load the <script> without blocking the DOM and make the <script> tag work like as a <img> tag to the parser as described above. This means that the script will be fetched in the background and continue parsing as normal without waiting. Okay, that seems fair but what's the difference between them...? Here we go then.

Async vs Defer

Both async and defer look like they do the same thing at the first glance, but that's not the case, there is a subtle difference between the two.

Defer waits for the DOM but Async doesn't -

The first and most important difference Is async doesn't care whether DOM is fully loaded or not, on the other side defer waits for the DOM to get loaded first and after that, it starts execution of the scripts.

For example, let's say you have 25000 buttons in your HTML document and now select every button of the DOM using both of the scripts and get the length of them.

<head>
  <script src="defer.js" defer></script>
  <script src="async.js" async></script>
</head>
<body>
  <div class="container">
  <!-- 25000 buttons -->
  </div>
</body>

Enter fullscreen mode Exit fullscreen mode

Here are our script codes...

//! Async script code here
let asyncButton = document.querySelectorAll('button');
console.log(`Async script button count: ${asyncButton.length}`);

Enter fullscreen mode Exit fullscreen mode
// Defer script code here
let deferButton = document.querySelectorAll('button');
console.log(`Defer script button count: ${deferButton.length}`);

Enter fullscreen mode Exit fullscreen mode

And here is the console output...

temp1.png

As you can see now, async is not waiting for the DOM to get loaded fully and selecting all the buttons loaded at the time of execution of the script and on the other hand, defer is waiting for the DOM elements to get loaded first and that's why it's selecting every button presented at the DOM.

If your script is dependent on the DOM, then never ever use the async attribute, there's the possibility that the element you need get undefined and this is a potential source of bugs.

Defer maintain the order of JS files Async doesn't -

What does it mean though, take another example to understand it? Let's say you will have four scripts. Each script logs the number of that script. Now if we gonna use the async attribute in the scripts, the order of executing scripts become unpredictable.

    <script src="one.js" async></script>
    <script src="two.js" async></script>
    <script src="three.js" async></script>
    <script src="four.js" async></script>

Enter fullscreen mode Exit fullscreen mode

The console output will be something like this...

temp2.png

But what if we use the defer attribute in every script?

    <script src="one.js" defer></script>
    <script src="two.js" defer></script>
    <script src="three.js" defer></script>
    <script src="four.js" defer></script>

Enter fullscreen mode Exit fullscreen mode

And here is the output...

temp3.png

So now you can see clearly that

defer always maintain the order of the script so if you have scripts that depend on each other then always consider using defer rather than async.

Conclusion

  • Adding the defer attribute will make sure DOM gets loaded first and then the scripts get executes in the given order.
  • Adding the async attribute will execute the script as soon as it gets loaded and this will not follow any order. So avoid using it if your script is dependent on each other or DOM.
  • So practically, defer is more useful than async, and most of the time you would want defer instead of async.
  • async is great when you want to load the script in the middle.

That's all about async and defer and script loading in JavaScript. If you enjoyed this article, please tell a friend about it or share it on your social media handles and make sure you comment below and share your thoughts about it. Thank you.🙏

Follow me on Twitter

Discussion (4)

Collapse
marzelin profile image
Marc Ziel

Modern browsers 'pre-parse' html to find all external resources and they start downloading them immediately (although with different priorities). So classic scripts don't block downloading other resources that are further in the HTML (only parsing is blocked).

There isn't much difference between deferred scripts and scripts placed before </body> tag - they're both evaluated just before DOMContentLoaded event but deferred scripts can be seen sooner (when they are placed in the <head>) and therefore start downloading a bit earlier.

Scripts with type=module (script modules) are deferred by default (but they can be async if needed).

Collapse
jeetsdev profile image
Ranjeet Singh Author • Edited

Hey, I wasn't aware of the 'pre-parse' technique of browsers. Yeah, there's not that much difference in placing script before </body> tag and differed script. We can still use our old school method without any guilt. Thanks for you clarification buddy.

Collapse
ramu profile image
Ramu

Thank you, the article was much useful..

Collapse
jeetsdev profile image
Ranjeet Singh Author

Glad to hear that buddy.