Nowadays, the optimization of the loading times of your web page is very important in many aspects. If your website loads slowly, Google will not rank your website very well and so the users, as they probably won't come back to a web page that takes minutes to load properly for the really important thing they're looking for, the information. You need to keep in mind that not everyone has access to high-speed connections, South-America is the best example of this situation. In developed countries the history is totally different, however, you shouldn't forget as well the people with mobile connections (4G) that although is fast, sometimes it isn't that fast as you would expect.
On a web page, the script elements (<script src="somescript.js"></script>
) are usually the most common resources that block directly the rendering and analysis of the HTML document. As the browser, when it starts to parse the HTML and founds a script tag, it waits for the script download and executes it, only then, it processes the rest of the page.
Fortunately for the user, there are 2 attributes for the script tag that can help to solve this problem, specifically defer
and async
.
A. <script>
Including a script without async or defer is the default way to load scripts in the HTML document. Through this method:
<script src="somescript.js"></script>
When the HTML parser processes this tag, a new request will be executed to fetch the somescript.js
file in the server. Once the download finishes, the script is immediately executed. As soon as it's executed, the rest of the document is parsed by the HTML parser. This is especially harmful when the developers place their scripts in the head tag:
<html lang="en-US">
<head>
<title>Our Code World</title>
<script src="somescript.js"></script>
</head>
<body>
<h3>Hello World!</h3>
...
</body>
</html>
So, until the user can see that title, a good time will have passed. You can graphically understand this behavior with the following graphic:
B. <script async>
By including a script with the async attribute, the script is downloaded in parallel with the interpretation of the HTML. When the script is downloaded, it's executed, blocking the rendering until it finishes.
In this approach, the execution order of the scripts isn't guaranteed as it would totally depend on which one is downloaded first, which may vary sometimes. So for example if you load the following scripts in the same order:
- script1.js
- script2.js
- script3.js
Sometimes, the execution would vary:
- script2.js
- script1.js
- script3.js
So ideally, when using async, the scripts shouldn't depend on each other or things will be messy. You can graphically understand this behavior with the following graphic:
C. <script defer>
Finally, the deferred script will be downloaded asynchronously and in parallel with the HTML parsing, however, its execution is deferred until the whole HTML document has been parsed. In this case, if multiple scripts are loaded, the same loading order will be respected when executing it.
You can graphically understand this behavior with the following graphic:
So, what's the best one?
It depends!
I know, it's the kind of answer that you'll find everywhere if you ask yourself this question. You need to know that neither async nor defer guarantees non-blocking the rendering as at the end of all, it is up to you and your script.
Well, implementing either async or defer will make your loading times automatically better than before (if implemented properly). However, there's an interesting structure that although it doesn't look good in our programmer's eyes, it does behave quite optimal on slow networks:
<html lang="en-US">
<head>
<title>Our Code World</title>
<!-- 1. Load CSS -->
<link href="stylesheet1.css" rel="stylesheet">
<link href="stylesheet2.css" rel="stylesheet">
<!-- 2. Load Deferred JS -->
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
<script src="script3.js" defer></script>
</head>
<body>
<!-- In this way the user will be able to access the content of the article way faster than usual -->
<h3>Article Title</h3>
<p>This is the important content that I want to see, not fancy menus or ads!</p>
</body>
</html>
That's right, including the deferred script in the head tag of the document will behave way faster for the users with slow connections. According to the following tests, loading our own blog (ourcodeworld.com with a size of about 700KB of CSS and JavaScript) with different connection speeds showed the following results for the domInteractive
event (this property can be used to measure the speed of loading Web sites that users feel):
# Test (Speed) | Deferred Script In Head | Deferred Script at the end of Body |
#1 (30KB/s) | 50 Seconds | 1 Minute 41 Seconds |
#2 (150KB/s) | 16 Seconds | 27 Seconds |
#3 (500KB/s) | 3.7 Seconds | 6.3 Seconds |
#4 (2000KB/s) | 1.89 Seconds | 1.75 Seconds |
As you can see, the approach of loading the deferred scripts in the head allows the domInteractive
event to show up faster, so in the case of our blog, the users with the mentioned speeds will see the content of the articles in the time that this test shows. You can see this table represented in the following chart:
As another conclusion, of this test, you can notice that the higher the internet speed connection, the less important it is the position of the deferred scripts, however, there's a visible improvement with slower connection speeds.
The problem with this approach is, that although it works incredibly great with a webpage like a blog, implementing the same approach on something like a single page application like React or Vue.js wouldn't make sense, isn't it? That's why they use the default approach of including the JavaScript at the end of the body tag.
Conclusions
- The scripts marked with
defer
are executed right after the domInteractive event, which happens after the HTML is loaded, parsed and the DOM is mounted. Once this is done, the browser will emit the domComplete event, and then onLoad. - Async and defer are basically two boolean attributes for the
<script>
tag. Async allows the execution of scripts asynchronously as soon as they're downloaded. Defer allows execution only after the whole document has been parsed. - Both attributes don’t have any effect on inline scripts.
Happy coding ❤️!