As a web developer, how many times you have written this line in your code?
<script src="script.js"></script>
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>
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>
Here are our script codes...
//! Async script code here
let asyncButton = document.querySelectorAll('button');
console.log(`Async script button count: ${asyncButton.length}`);
// Defer script code here
let deferButton = document.querySelectorAll('button');
console.log(`Defer script button count: ${deferButton.length}`);
And here is the console output...
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>
The console output will be something like this...
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>
And here is the output...
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 usingdefer
rather thanasync
.
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 thanasync
, and most of the time you would wantdefer
instead ofasync
. -
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.🙏
Top comments (4)
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 beforeDOMContentLoaded
event butdeferred
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 beasync
if needed).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 the clarification buddy.Thank you, the article was much useful..
Glad to hear that buddy.