Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS scripts not downloaded and evaluated sequentially

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)?

like image 630
Nitkov Avatar asked Jun 01 '21 09:06

Nitkov


People also ask

Are script tags loaded in order?

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.

In which sequence JavaScript files are 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 ...

Does script order matter JavaScript?

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.

How do browsers load scripts?

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.


Video Answer


2 Answers

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>
like image 185
Lajos Arpad Avatar answered Oct 19 '22 00:10

Lajos Arpad


Option 1: defer

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>

Option 2: Managed Sequential Loading

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
    });
}
like image 29
Kinglish Avatar answered Oct 19 '22 01:10

Kinglish