I have 3 <script>
tags as the last elements in <body>
. They load external .js scripts:
...
</div>
<script src="https://somewhere.com/scripts/script1.js"></script>
<script src="https://somewhere.com/scripts/script2.js"></script>
<script src="https://somewhere.com/scripts/script3.js"></script>
</body>
</html>
All scripts contain only one line: console.log(Start script X)
, where X is the script number.
I was expecting for the scripts to be evaluated sequentially and the output to be:
Start script 1
Start script 2
Start script 3
But the output seems random. To be more precise, it looks like the order of evaluation depends solely on the order in which the scripts finish downloading.
Was I right to expect evaluation order to mimic the order they were referenced in HTML? If not, how do I make sure that they are evaluated in the correct order (besides merging them all in one .js file)?
Script tags are executed in the order they appear It also means scripts which appear later on the page can depend on things scripts which appear earlier have done. Elements on the page won't render until all the script tags preceding them have loaded and executed.
The Javascript code on the page is part of the HTML document, so the order in which Javascript is loaded is the order in which the tag < script /> appears, and the external JS in the < script /> tag or introduced through src is executed in the order in which the statement appears, and the execution process is part of ...
If I'm understanding your question I think you're asking if it matters where in a file a function/method is defined, and the answer is no, you can define them anywhere in a single source file. The JavaScript parser will read in all symbols before trying to run the code.
In order to assess the consequences of any such decision, it helps to understand how browsers work: When the browser processes an HTML document, it does so from top to bottom. Upon encountering a <script> tag, it halts (“blocks”) further processing[2] in order to download the referenced script file.
Your observation is correct. I understand your expectation, but such ordering of evaluation would imply either that the files are downloaded in sequence rather than in paralell, or that they are evaluated in sequence rather than in paralell.
Rendering them to be downloaded or executed in sequence would be troublesome. What if a script never downloads? Should the subsequent scripts hang on forever? It would be much worse than the current situation.
What if the scripts are not interdependent? Should they wait for each-other even if there is no point in doing so? Of course not.
So the actual behavior is correct, but you are right that this implies some problems as well.
In general, the solution for this problem is to break up the scripts into classes and functions that can be executed when it is ideal, rather than at the time of the script download and also have an onload
event in the body
tag or a load
event
being created via AddEventListener
.
Let's assume that there is an f1
function
in script1, an f2
function
in script2, ...
then you can do something like this:
window.addEventListener('load', (event) => {
f1();
f2();
//...
});
You can even do it like this:
function load() {
f1();
f2();
//...
}
and then add it to the body
:
<body onload="load()">
<!-- ... -->
</body>
This article illuminates the defer/asyc script tag attributes. Scripts marked defer
are executed (after parsing completes) in the order which they are defined in the markup.
<script src="https://somewhere.com/scripts/script1.js" defer></script>
<script src="https://somewhere.com/scripts/script2.js" defer></script>
<script src="https://somewhere.com/scripts/script3.js" defer></script>
<script src="https://somewhere.com/scripts/script4.js" defer></script>
This script should load the files in one by one in order and your desired sequence should be preserved
const scripts = [
"https://somewhere.com/scripts/script1.js",
"https://somewhere.com/scripts/script2.js",
"https://somewhere.com/scripts/script3.js",
"https://somewhere.com/scripts/script4.js"
];
let sctr = 0;
const doScript = () => {
if (sctr++ >= scripts.length) {
console.log('all scripts loaded syncronously');
return;
}
const scriptPromise = new Promise((resolve, reject) => {
const script = document.createElement('script');
document.body.appendChild(script);
script.onload = resolve;
script.onerror = reject;
script.async = true; // they're loading one at a time, so async is ok
script.src = scripts[sctr];
});
scriptPromise.then(() => {
console.log(scripts[sctr], 'loaded');
doScript();
},
(err) => {
console.error(scripts[sctr], 'failed', err);
// halting
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With