Whenever the HTML parser requires a resource (js or css file) from the network, it notifies the network layer to fetch the resource.
The network layer, which a asynchronous module, downloads the resource and gives it to the synchronous html parser. Note that the resources from <style> tag (css files) are fetched asynchronously and the resources of <script> tag (js files) are fetched synchronously, meaning, the html parser is stopped until the resource is fetched and executed.
It is recommended to place the script tags at the end of the html document, right before </body>. Now, the html parser would’ve formed the DOM tree, before it stops to execute the script files. This ensures that all DOM elements required in the script is created in the DOM tree before the execution of the script files.
However, this can be furthermore optimised using the attributes of script tag like async and defer.
Differences between async and defer
Consider this gist of angular-webpack boilerplate,
Two external JS files are referenced in the lines 10 & 11 respectively,
- vendor.js — contains the third party code of your project. (angular.js)
- app.js — contains all the JS files in the src folder of the app.
Let’s see what happens in the following cases,
- Using script tags without DEFER or ASYNC.
- Using ASYNC in script tag
- Using DEFER in script tag
1. Using script tags without DEFER or ASYNC.
- The parser sends request to network layer to download the file at line no. 10 (vendor.js).
- HTML parser is paused, till the script file is downloaded and executed. The same process is repeated for app.js
NOTE: In the diagram, the HTML parsing is complete when the yellow bar ends.
2. Using ASYNC in script tag
- The parser sends request to network layer to download the file, at line no. 10 & and it continues its execution, till the script file is downloaded.
- The parser reads the next line and sends request for app.js. It doesn’t care if vendor.js is downloaded or not.
- But once the script file is downloaded, the html parser pauses and script file is executed. The same process is repeated for app.js
Now, let’s see what happens if the app.js is downloaded sooner than vendor.js.
- The request to download the two files is sent synchronously. But the download happens asynchronously, and which ever file gets downloaded first, will be executed first.
- Async attribute does not ensure the order of execution, which sometimes makes our app to be erroneous.
3. Using DEFER in script tag
- The parser sends request to network layer to download vendor.js at line no. 10 and continues to the next line.
- The html parser waits neither for download nor execution of script files.
NOTE: One of the Pros of DEFER is that the order of execution of script files, is ensured.
Even, if the download completes before the html parsing is complete, the execution of script tags is only done after the html is parsed completely. This way, the time required for html parsing is greatly reduced.
The browsers usually measure the loading time as one of the key factors to rate the performance. It is usually measured by the time taken to parse the whole html.
In the above diagrams, the time taken to parse the HTML is indicated by the the yellow bar. DEFER is a clear winner here.
To check the time taken to parse the whole HTML is given by duration attribute of PerformanceNavigationTiming. It can be accessed by performance.getEntries().duration for any website.
Previously, the loading time was measured using DomInteractive event, which is now deprecated.
Although async blocks parsing of the html files, it does not say anything about rendering. Both DEFER and ASYNC do not guarantee anything on rendering.
The audit in chrome browser for a app that I’m part of, gave me these reports,
When do we use?
We could use DEFER in script tags and greatly reduce the loading time in the eyes of the browser.
However, in the modern single page applications, other than the vendor.js and the app.js, there isn’t much to be parsed in the HTML itself. So, this does not qualify for a great performance metric.
Only after the app.js is executed, the contents of the app is visible to the user.
Here are the examples of JS files for which the attributes can be used,
SCRIPT tags: vendor.js, app.js
DEFER tags: vendor.js, app.js, Analytics js
ASYNC tags: JS files which are independent, for eg., external components, which appears on the first page.
What do you think about how we could use these attributes efficiently for our modern single page applications ?
Let me know your thoughts on comments. :)