{"id":3230,"date":"2024-05-07T15:44:55","date_gmt":"2024-05-07T13:44:55","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=3230"},"modified":"2024-05-07T15:56:46","modified_gmt":"2024-05-07T13:56:46","slug":"scripts-attributes-async-vs-defer-optimize-our-javascript-loading","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2024\/05\/07\/scripts-attributes-async-vs-defer-optimize-our-javascript-loading\/","title":{"rendered":"Scripts attributes : async VS defer"},"content":{"rendered":"\n<p>When it comes to web development, the way we load and execute JavaScript can have a significant impact on the performance and user experience of our website.<\/p>\n\n\n\n<p>When the browser loads HTML and comes across a&nbsp;<code>&lt;script&gt;...&lt;\/script&gt;<\/code>&nbsp;tag, it can\u2019t continue building the DOM. It must execute the script right now. The same happens for external scripts&nbsp;<code>&lt;script src=\"...\"&gt;&lt;\/script&gt;<\/code>: the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page.<\/p>\n\n\n\n<p>That leads to two important issues:<\/p>\n\n\n\n<ol><li><em>Scripts can\u2019t see DOM elements below them, so they can\u2019t add handlers etc.<\/em><\/li><li><em>If there\u2019s a bulky script at the top of the page, it \u201cblocks the page\u201d. Users can\u2019t see the page content till it downloads and runs:<\/em><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p&gt;...content before script...&lt;\/p&gt;\n\n&lt;script src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js?speed=1\"&gt;&lt;\/script&gt;\n\n&lt;!-- This isn't visible until the script loads --&gt;\n&lt;p&gt;...content after script...&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>There are some workarounds to that. For instance, we can put a script at the bottom of the page. Then it can see elements above it, and it doesn\u2019t block the page content from showing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;body&gt;\n  ...all content is above the script...\n\n  &lt;script src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js?speed=1\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-red-color\">PS:<\/span><\/strong> <em><strong>But this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. <span class=\"has-inline-color has-vivid-red-color\">For long HTML documents, that may be a noticeable delay.<\/span><\/strong><\/em><\/p>\n\n\n\n<p>In other words:<\/p>\n\n\n\n<p>Such things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection.<\/p>\n\n\n\n<p>This is where script attributes come into play, and in this article, we&#8217;ll dive into the world of <strong><em>async <\/em><\/strong>and <strong><em>defer<\/em><\/strong>, two powerful tools to optimize our JavaScript loading.<\/p>\n\n\n\n<p><strong>PS:<\/strong> <em>async and defer are HTML attributes that we can apply to our tags to control how the browser handles the loading and execution of our JavaScript files.<\/em><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2024\/05\/async-defer-1.png\" alt=\"\" class=\"wp-image-3236\" width=\"514\" height=\"257\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2024\/05\/async-defer-1.png 678w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2024\/05\/async-defer-1-300x150.png 300w\" sizes=\"(max-width: 514px) 100vw, 514px\" \/><\/figure><\/div>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4>defer<\/h4>\n\n\n\n<p>The&nbsp;<code>defer<\/code>&nbsp;attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads \u201cin the background\u201d, and then runs when the DOM is fully built.<\/p>\n\n\n\n<p>Here\u2019s the same example as above, but with&nbsp;<code>defer<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p&gt;...content before script...&lt;\/p&gt;\n\n&lt;script defer src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js?speed=1\"&gt;&lt;\/script&gt;\n\n&lt;!-- visible immediately --&gt;\n&lt;p&gt;...content after script...&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<ul><li>Scripts with&nbsp;<code>defer<\/code>&nbsp;never block the page.<\/li><li>Scripts with&nbsp;<code>defer<\/code>&nbsp;always execute when the DOM is ready (but before&nbsp;<code>DOMContentLoaded<\/code>&nbsp;event).<\/li><\/ul>\n\n\n\n<p>The following example demonstrates the second part:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p&gt;...content before scripts...&lt;\/p&gt;\n\n&lt;script&gt;\n  document.addEventListener('DOMContentLoaded', () =&gt; alert(\"DOM ready after defer!\"));\n&lt;\/script&gt;\n\n&lt;script defer src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js?speed=1\"&gt;&lt;\/script&gt;\n\n&lt;p&gt;...content after scripts...&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<ol><li>The page content shows up immediately.<\/li><li><code>DOMContentLoaded<\/code>&nbsp;event handler waits for the deferred script. It only triggers when the script is downloaded and executed.<\/li><\/ol>\n\n\n\n<p>The <strong>defer<\/strong> attribute tells the browser to load the script in parallel with the HTML parsing, but to execute the script only after the HTML parsing is complete. <strong>This ensures that the script is executed in the order they appear in the HTML<\/strong>, which is important for scripts that have dependencies on each other or on the DOM structure.<\/p>\n\n\n\n<p>Let\u2019s say, we have two deferred scripts: the&nbsp;<code>long.js<\/code>&nbsp;and then&nbsp;<code>small.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script defer src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js\"&gt;&lt;\/script&gt;\n&lt;script defer src=\"https:\/\/javascript.info\/article\/script-async-defer\/small.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>Browsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The&nbsp;<code>small.js<\/code>&nbsp;probably finishes first.<\/p>\n\n\n\n<p>\u2026But the&nbsp;<code>defer<\/code>&nbsp;attribute, besides telling the browser \u201cnot to block\u201d, ensures that the relative order is kept. So even though&nbsp;<code>small.js<\/code>&nbsp;loads first, it still waits and runs after&nbsp;<code>long.js<\/code>&nbsp;executes.<\/p>\n\n\n\n<p>This is useful for scripts that need to access the DOM or rely on other scripts being loaded first, such as our application&#8217;s main JavaScript file.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4>async<\/h4>\n\n\n\n<p>The&nbsp;<code>async<\/code>&nbsp;attribute is somewhat like&nbsp;<code>defer<\/code>. It also makes the script non-blocking. But it has important differences in the behavior.<\/p>\n\n\n\n<p>The async attribute tells the browser to load the script asynchronously, meaning the script will be downloaded in parallel with the parsing of the HTML, <strong>and it will be executed as soon as it&#8217;s available, without blocking the HTML parsing<\/strong>.<\/p>\n\n\n\n<p>This is particularly useful for scripts that are independent of the initial page content and don&#8217;t need to wait for the DOM to be fully loaded, or any script that doesn&#8217;t have a direct dependency on the page&#8217;s content.<\/p>\n\n\n\n<p>In other words,&nbsp;<code>async<\/code>&nbsp;scripts load in the background and run when ready. The DOM and other scripts don\u2019t wait for them, and they don\u2019t wait for anything.<\/p>\n\n\n\n<ol><li>The browser doesn\u2019t block on&nbsp;<code>async<\/code>&nbsp;scripts (like&nbsp;<code>defer<\/code>).<\/li><li>Other scripts don\u2019t wait for&nbsp;<code>async<\/code>&nbsp;scripts, and&nbsp;<code>async<\/code>&nbsp;scripts don\u2019t wait for them.<\/li><li><code>DOMContentLoaded<\/code>&nbsp;and async scripts don\u2019t wait for each other:<\/li><\/ol>\n\n\n\n<p>Here\u2019s an example similar to what we\u2019ve seen with&nbsp;<code>defer<\/code>: two scripts&nbsp;<code>long.js<\/code>&nbsp;and&nbsp;<code>small.js<\/code>, but now with&nbsp;<code>async<\/code>&nbsp;instead of&nbsp;<code>defer<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p&gt;...content before scripts...&lt;\/p&gt;\n\n&lt;script&gt;\n  document.addEventListener('DOMContentLoaded', () =&gt; alert(\"DOM ready!\"));\n&lt;\/script&gt;\n\n&lt;script async src=\"https:\/\/javascript.info\/article\/script-async-defer\/long.js\"&gt;&lt;\/script&gt;\n&lt;script async src=\"https:\/\/javascript.info\/article\/script-async-defer\/small.js\"&gt;&lt;\/script&gt;\n\n&lt;p&gt;...content after scripts...&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<ul><li>The page content shows up immediately:&nbsp;<code>async<\/code>&nbsp;doesn\u2019t block it.<\/li><li><code>DOMContentLoaded<\/code>&nbsp;may happen both before and after&nbsp;<code>async<\/code>, no guarantees here.<\/li><li>A smaller script&nbsp;<code>small.js<\/code>&nbsp;goes second, but probably loads before&nbsp;<code>long.js<\/code>, so&nbsp;<code>small.js<\/code>&nbsp;runs first. Although, it might be that&nbsp;<code>long.js<\/code>&nbsp;loads first, if cached, then it runs first. In other words, async scripts run in the \u201cload-first\u201d order.<\/li><\/ul>\n\n\n\n<p>Async scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don\u2019t depend on our scripts, and our scripts shouldn\u2019t wait for them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!-- Google Analytics is usually added like this --&gt;\n&lt;script async src=\"https:\/\/google-analytics.com\/analytics.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p><span class=\"has-inline-color has-vivid-red-color\"><strong>PS<\/strong>: <\/span><strong>The&nbsp;<code>defer<\/code>,async&nbsp;attributes are only for external scripts<\/strong>. The&nbsp;attribute are ignored if the&nbsp;<code>&lt;script&gt;<\/code>&nbsp;tag has no&nbsp;<code>src<\/code>.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4>Dynamic scripts<\/h4>\n\n\n\n<p>There\u2019s one more important way of adding a script to the page.<\/p>\n\n\n\n<p>We can create a script and append it to the document dynamically using JavaScript:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>let script = document.createElement('script');\nscript.src = \"\/article\/script-async-defer\/long.js\";\ndocument.body.append(script); \/\/ (*)<\/code><\/pre>\n\n\n\n<p>The script starts loading as soon as it\u2019s appended to the document&nbsp;<code>(*)<\/code>.<\/p>\n\n\n\n<p><strong>Dynamic scripts behave as \u201casync\u201d by default.<\/strong><\/p>\n\n\n\n<p>This can be changed if we explicitly set&nbsp;<code>script.async=false<\/code>. Then scripts will be executed in the document order, just like&nbsp;<code>defer<\/code>.<\/p>\n\n\n\n<p>In this example,&nbsp;<code>loadScript(src)<\/code>&nbsp;function adds a script and also sets&nbsp;<code>async<\/code>&nbsp;to&nbsp;<code>false<\/code>.<\/p>\n\n\n\n<p>So&nbsp;<code>long.js<\/code>&nbsp;always runs first (as it\u2019s added first):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function loadScript(src) {\n  let script = document.createElement('script');\n  script.src = src;\n  script.async = false;\n  document.body.append(script);\n}\n\n\/\/ long.js runs first because of async=false\nloadScript(\"\/article\/script-async-defer\/long.js\");\nloadScript(\"\/article\/script-async-defer\/small.js\");<\/code><\/pre>\n\n\n\n<p>Without&nbsp;<code>script.async=false<\/code>, scripts would execute in default, load-first order (the&nbsp;<code>small.js<\/code>&nbsp;probably first).<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h5>References:<\/h5>\n\n\n\n<p><a href=\"https:\/\/javascript.info\/\">https:\/\/javascript.info\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When it comes to web development, the way we load and execute JavaScript can have a significant impact on the performance and user experience of our website. When the browser loads HTML and comes across a&nbsp;&lt;script&gt;&#8230;&lt;\/script&gt;&nbsp;tag, it can\u2019t continue building the DOM. It must execute the script right now. The same happens for external scripts&nbsp;&lt;script [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3234,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[5],"tags":[417,761,303,839,7],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3230"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=3230"}],"version-history":[{"count":4,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3230\/revisions"}],"predecessor-version":[{"id":3237,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3230\/revisions\/3237"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/3234"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=3230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=3230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=3230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}