In the first code example, the browser waits for index-2.js
to load and execute before running ANY further scripts and before displaying your page. This is said to be a "blocking"
load. It blocks further progress by the parser until the script is loaded.
In the second code example (the script inserted with .appendChild()
), the browser does not wait for index-2.js
to load or run before proceeding with other scripts or before displaying your page. Here index-2.js
is loaded asynchronously while other things in the page continue to be processed. This is said to be a "non-blocking"
load. It does not block further progress by the browser while it is loading. Once it finishes loading the dynamic script in the background, the browser is free to execute it whenever it wants to (likely when it has nothing else to do at the moment). When it does actually execute it will vary by browser and is not governed by specification.
So, the timing has nothing to do with index-1.js
. That is a synchronous script tag. It is blocking in both examples. The browser will not process past that <script>
tag.
The difference is in what happens after you insert index-2.js
.
Execution order is what is defined by specification so that is what you have to concentrate on or measure. It is up to the browser when it actually decides to load a script over the network so you can't use the loading bar chart to see the execution. Obviously, it can't start loading it before the parser knows about the script and it has to load it before it is required to execute it, but within that, the browser can use its own logic to decide when to load it. Because each browser has limits on the number of simultaneous resources that it will attempt to download from one host, exactly when it decides to download the script could easily vary from one browser to the next.
And, keep in mind that a non-blocking script inserted with .appendChild()
can run any time the browser wants. It can run nearly immediately (if the browser has that script in its cache and has nothing else to do because it's waiting on other resources) or it can run it near the end of the whole loading process. When you insert it with .appendChild()
, you are instructing the browser not to block the current parsing and loading of the page to run this script, but as soon as the browser has the contents of that script, it can run it whenever it feels like it.
When you insert it with document.write()
, you are instructing the browser to run it as soon as the current script tag is finished running which obviously means it has to be loaded immediately too.
If you put this:
<script src="index-3.js"></script>
right before the <link>
tag, then your timeline would tell the whole story as you would see index-3.js
run sooner in the second code example because it would not wait for index-2.js
to load and run. In fact, if you put console.log()
entries in each of index-1.js
, index-2.js
and index-3.js
, then you could see the difference in execution order.
FYI, you can also use the async
and defer
attributes in <script>
tags to make an inline <script>
tag be non-blocking. More discussion of those options here: load and execute order of scripts.