Using the async and defer script tag attributes

OpenJavaScript 0
Reading Time: 4 minutes 🕑

Last updated: March 2, 2022.

The async and defer attributes for script tags are new to HTML5 and help in structuring the loading of JavaScript linked to a web page.

Structuring the loading of scripts is important not only to ensure that a page loads correctly, but, when optimized, can also improve overall page loading speed, which is a direct ranking factor for placement of a page in search engine results.

In this article, we look at how adding async and defer attributes to your script tags can help structure page loading, and how this differs from the default behavior of a script tag without either attribute.

FeatureNo attributeAsyncDefer
Blocks loading of page while script downloads
Blocks loading of page while script executes
Script is run after DOM is fully parsed
Scripts load in sequence they appear in HTML
Scripts can load in an unpredictable way

Using <script>

A regular script tag without the async or defer attribute is very blocking in its behavior.

When the DOM-parser in a browser encounters it, it stops parsing the page entirely.

While it stops parsing, it downloads the script it has encountered and runs it. Page loading only resumes again when this process is complete.

Therefore, a regular script tag has the potential to badly affect page loading speed by holding up the entire rest of the content of the page from loading while its own contents is downloaded and processed.

For this reason, it is usually best to place ordinary script tags just before the closing body tag. This avoids blocking the loading of DOM content within the body and ensures also that this content is available to it to access and manipulate.

In fact, this simple solution can often be the best one.

But note that multiple scripts still block each other from loading! This can be desirable: it ensures that scripts execute in order. But it is likey sub-optimal for multiple scripts that do not need to execute in order.

When to use an ordinary script tag:

  • ✔️ For non-interference with initial page loading speed (if placed before the closing body tag)
  • ✔️ For accessing DOM elements
  • ✔️ To execute multiple scripts in order
  • ✔️ For high-priority (but blocking) behavior

Using <script async>

Adding the async attribute to a script tag changes in behavior in the following way: when the DOM-parser encounters the script, it does not stop parsing HTML. Instead, it continues parsing HTML at the same time as downloading the script.

When the script has fully downloaded, then the DOM-parser immediately stops parsing HTML and runs the script (blocking page loading). Once the script has been executed, it returns to parsing HTML again.

Note that it is impossible to be certain about when a script will download. And it is therefore impossible to know when a script with the async attribute will be run.

So, with a script with an async tag, it is generally a bad idea to attempt to manipulate the DOM, unless it is placed at the end of a HTML document.

It is also generally a bad idea to use async with multiple scripts that should execute in order, because the execution order is ‘downloaded-first’, not necessarily the order they appear in HTML.

But the async tag is ideal for loading multiple scripts that do not depend upon one another. And, if placed in the head, scripts that do not modify the DOM.

But note that script downloading immediately. So although a script with the async attribute is non-blocking, it can expend potentially precious bandwidth on downloading a script upon initial page loading. This may or may not be desirable, depending upon script importance.

When to use the async attribute:

  • ✔️ To increase efficiency of multiple scripts that do not need to complete in order
  • ✔️ If placed in the head tag, for scripts that are important to page loading and do not manipulate DOM nodes and elements

Using <script defer>

When a browser encounters a script tag with the defer attribute added, it will start to download the script immediately without blocking DOM-parsing.

Unlike async, though, the script is not run as soon as it has downloaded. Instead, it is only executed when DOM-parsing is fully complete. This guarantees that a script tag with the defer attribute will have full access to the DOM, even if it is placed in the head of a HTML document.

Another feature of a script tags with the defer attribute is that they execute in the order they are placed in a HTML document. This is when script order execution is a necessity.

A potential disadvantage of a defer script tag, however, relates to its immediate downloading of a script. This can negatively impact initial page loading, and may be unnecessary for a non-crucial script (e.g. user analytics).

When to use the defer attribute:

  • ✔️ For placing important script tags in the head of a document
  • ✔️ For scripts that manipulate the DOM
  • ✔️ To guarantee that multiple scripts execute in order

Using async and defer attributes

If you apply the async and defer attributes to the same script that, async will be ignored in favor of the defer attribute:

The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default.

World Wide Web Consortium (W3C)

However, because all modern browsers now support the async and defer attributes, this is in practice no longer necessary. It is therefore not recommended to apply both to the same script tag.