skip to content
Nikolas Barwicki - Javascript Blog Nikolas's Blog

Async vs defer - HTML Script tags

/ 5 min read

The Importance of Script Tag Placement

Traditionally, script tags were positioned within the <head> section of an HTML document. However, this placement was not without its drawbacks. The browser would halt the parsing of the HTML document to fetch and execute the script. This pause could lead to a noticeable delay in the loading of the webpage, negatively impacting the user experience.

To counteract this issue, developers started placing scripts at the bottom of the page, just before the closing </body> tag. This allowed the HTML document to be fully parsed and the webpage to load before the script was executed. Although this mitigated the issue of page loading delays, it was not a foolproof solution. The execution of scripts could still potentially block the rendering of the webpage, and the order of script execution was not guaranteed.

Enter async and defer

To address these challenges, the async and defer attributes were introduced. These boolean attributes, which can be added to the script tag, allow scripts to be fetched asynchronously, further enhancing the loading performance of webpages.

The async attribute allows the script to be fetched in the background while the HTML document continues to be parsed. However, once the script is ready, the HTML parsing is paused, and the script is executed. This means that the execution order of multiple scripts with the async attribute is not guaranteed — they will be executed in the order they are fetched and ready, which might not be the same as the order in the HTML document.

Here’s an example of how the async attribute is used:

<script async src="example-script.js"></script>

On the other hand, the defer attribute also allows the script to be fetched in the background, but unlike async, the script execution is deferred until the HTML document has been fully parsed. This ensures that scripts are executed in the order they appear in the HTML document.

Here’s an example of how the defer attribute is used:

<script defer src="example-script.js"></script>

async vs defer: A Performance Comparison

Without using async or defer, placing a script in the <head> of the document pauses the parsing of the HTML document until the script is fetched and executed. This can lead to longer page loading times, especially for larger scripts or slower network connections.

If the script is placed at the end of the <body> tag without async or defer, the HTML document parsing finishes before the script is fetched and executed. This can improve page loading time but might delay the script execution, which could be a problem if the script is critical for the webpage.

When using the async attribute in the head, the script is fetched asynchronously, and the HTML parsing is only paused when the script is ready to be executed. This can lead to faster page loading times, but the execution order of scripts is not guaranteed.

When the defer attribute is used in the head, the script is fetched asynchronously, and the execution is deferred until the HTML parsing is complete. This guarantees the execution order of scripts and allows the browser to continue parsing the HTML document while the script is being fetched, leading to potentially faster page loading times.

Blocking Parsing and Rendering

It’s important to note that the async attribute blocks HTML parsing while the script is being executed. In contrast, the defer attribute does not block HTML parsing, leading to potentially faster page loading times. Neither async nor defer make guarantees about blocking rendering — this largely depends on the implementation of the script itself.

domInteractive and Execution Order

Scripts with the defer attribute are executed after the domInteractive event. This event is triggered after the HTML document is loaded and parsed, and the Document Object Model (DOM) is built. In contrast, scripts with the async attribute are executed as soon as they are ready, potentially before the domInteractive event. This means that scripts with the async attribute can be executed out of order, while scripts with the defer attribute maintain their relative order as defined in the HTML document.

Best Practices for Using async and defer

To optimize page loading performance, it’s generally recommended to place scripts in the <head> of the document and add the defer attribute to the script tag. This allows the script to be fetched asynchronously while the HTML document is being parsed, and the script execution is deferred until after the parsing is complete. This approach triggers the domInteractive event earlier, improving the perceived page loading speed.

However, there might be cases where the async attribute is more suitable. If a script is independent, meaning it doesn’t rely on any other scripts, and the order of execution does not matter, the async attribute might be a better choice. This allows the script to be executed as soon as it’s ready, potentially speeding up the time until the script is run.

Here’s a best practice example of how to use defer:

<head>
  <script defer src="script1.js"></script>
  <script defer src="script2.js"></script>
</head>

In this example, both scripts are fetched asynchronously while the HTML document is being parsed. The scripts are executed after the HTML parsing is complete, in the order they appear in the document. This ensures that script1.js is executed before script2.js, even if script2.js finishes fetching first.

By understanding the differences between async and defer script tags, developers can make informed decisions about which attribute to use to optimize script loading and execution, enhancing the user experience and improving the performance of their webpages.