How Javascript Files Load (Normal vs Async vs Defer)

INTRODUCTION

THE LOADING MYSTERY

Welcome to a quick tutorial on how Javascript files are loaded – Normal, async, and defer. Ever since day one of learning Javascript, we were taught to either code Javascript inline or load them from external files. But just how are these Javascript files loaded? This simple question has somehow eluded many beginners, and in short point form:

  • Javascript is normally loaded in the order of top-to-bottom, left-to-right.
  • They are normally blocking; One script has to be fully loaded and finished running before the next can proceed.
  • When set to load in async, loading will be done in parallel, run once fully loaded.
  • When set to defer, loading will be done in parallel, but run only after the page is fully loaded.

Still confused? Then let us walk through some examples in this guide – Read on!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

 

PREAMBLE

DOWNLOAD & NOTES

First, here is the download link to the example source code as promised.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

QUICK NOTES

There is nothing to install, so just download and unzip into a folder. If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

SECTION A

NORMAL SCRIPT LOADING

First, let us start with how the “basic” scripts are being “normally” loaded in an HTML document.

 

TOP-TO-BOTTOM, LEFT-TO-RIGHT

To demonstrate the loading order of scripts, let us create a dummy page.

1a-load-order.html
<script src="1b-first.js"></script>
<script>console.log("Second");</script>
<script src="1c-third.js"></script>
1a-first.js
console.log("First");
1c-third.js
console.log("Third");

Next, we open this example into the web browser and will get the following result.

First
Second
Third

Yep, scripts are normally loaded and executed in the order of top-to-bottom, left-to-right. If we open up the “network” tab (or waterfall chart) in the developer’s console, it will also show this loading order:

 

 

THE LOADING SEQUENCE PROBLEM

The above loading order of the scripts should be easy enough to understand, but here is a very common beginner pitfall to take extra note of.

2a-bad-seq.html
<script>
  document.getElementById("para-paragraph").innerHTML = "Added with Javascript";
</script>
<p id="para-paragraph"></p>

Both the script and HTML elements are in place, that should not be a problem now – Right? Well, here is what happens when we run the above example:

Uncaught TypeError: Cannot set property 'innerHTML' of null at 2a-bad-seq.html:6

As you can see, Javascript cannot find the #para-paragraph element and throws an exception instead; The script loaded and executed before the element is properly parsed. So now that you know, be careful with the sequence of where you place the scripts. HTML elements must be properly parsed before we can access them in Javascript.

2b-good-seq.html
<p id="para-paragraph"></p>
<script>
document.getElementById("para-paragraph").innerHTML = "Added with Javascript";
</script>

 

THE BLOCKING PROBLEM

Lastly, let us follow up a little more with 2a-bad-seq.html above. What basically happened is:

  • The web browser will load and run the <script> first.
  • The  <p id="para-paragraph"> (and everything below) cannot proceed to parse until the <script> is fully loaded and finished running.

This is a common problem known as a “blocking Javascript” – Where the rest of the page cannot continue to load and has to wait for the script to be done first.

 

 

 

SECTION B

ASYNC & DEFER

Blocking is not a big deal if the scripts are small and lean. But it has become a huge problem when fat Javascripts prevent the rest of the page from rendering, slowing the loading to a crawl. To combat this blocking problem, the smart master code ninjas came up with 2 mechanisms in HTML5 – Async and defer.

 

ASYNCHRONOUS LOADING

Following up with the above example (yet again), let us add an async property to the <script> tag this time – Take note, async can only be applied to external scripts.

3a-async.html
<script async src="3b-script.js"></script>
<p id="para-paragraph"></p>
3b-script.js
document.getElementById("para-paragraph").innerHTML = "Added with Javascript";

And it works! What kind of sorcery is this? Very simply, the async property loads the Javascript in parallel as the HTML elements continue to parse. It will only run once the script is fully loaded.

 

DEFERRED LOADING

The async way of loading is cool, but it has one problem – We cannot fully grasp the loading time of the script. That is, the above example will still throw an error if #para-paragraph is not rendered when the script is fully loaded. So this is where defer steps in, loading the script asynchronously and only running when the page is fully loaded.

4a-defer.html
<script defer src="4b-script.js"></script>
<script>
console.log("This section will run first because it is blocking.");
</script>
<p id="para-paragraph"></p>
4b-script.js
document.getElementById("para-paragraph").innerHTML = "Added with Javascript";
console.log("This will be deferred to the end.");

P.S. defer can only be applied to external scripts only.

 

 

MULTIPLE DEFER

Now for the final million-dollar question – What happens when we defer multiple scripts? Which one will run first?

5-multi-defer.html
<script defer src="1b-first.js"></script>
<script defer src="1c-third.js"></script>
<script>
  console.log("This blocking script will definitely run first.");
</script>

The result is:

This blocking script will definitely run first.
First
Third

Yep – It’s simple. The deferred scripts will run in the exact same order, “first come first serve” basis.

 

EXTRA

USEFUL BITS & LINKS

That’s all for this tutorial, and here is a small section on some extras and links that may be useful to you.

 

ASYNC DEFER CALLBACK ON LOAD

This is a rather interesting question that I found on the Internet. How do we add a callback when an async or defer script is ready? Very simply, we can apply an onload property to it – Yes, this is legit and not a hack.

6-callback.html
<!-- (A) SCRIPT READY FUNCTION -->
<script>
function ready (whichone) {
  console.log(whichone.src);
}
</script>

<!-- (B) APPEND ONLOAD ATTRIBUTE -->
<script defer src="1b-first.js" onload="ready(this)"></script>
 
<!-- (C) DYNAMIC JS LOAD JS -->
<script>
var script = document.createElement('script');
script.setAttribute("async", "");
script.src = "1c-third.js";
script.onload = function () {
  console.log(this.src);
};
document.head.appendChild(script);
</script>

 

 

SUMMARY

  • Scripts are loaded in the order as placed in the HTML document – From the top-to-bottom, left-to-right.
  • Normal scripts – Load and run “as-it-is”, will block the loading of elements and scripts below.
  • Async scripts – Load in parallel, run when the script is loaded.
  • Defer scripts – Load in parallel, run after the entire page is loaded.

 

LINKS & REFERENCES

 

INFOGRAPHIC CHEAT SHEET

How Javascript Files Load (Click to Enlarge)

 

CLOSING

WHAT’S NEXT?

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *