Reputation: 53
I'm struggling with loading JS files in sequential order, despite having looked at various other SO posts and documentation on async
and defer
. My code structure is as follows:
<script src="lang.js"></script> <!--dynamically loads either eng.js or fra.js-->
<script src="task1.js"></script> <!--all of the task*.js depend on eng.js/fra.js-->
<script src="task2.js"></script>
<script src="task3.js"></script>
<script src="task4.js"></script>
<script>
// inline JS; depends on all task*.js
</script>
The contents of lang.js
are as follows:
let langScript = document.createElement("script")
// FR is a boolean defined earlier
langScript.setAttribute("src", FR ? "fra.js" : "eng.js");
langScript.setAttribute("async", "false");
let head = document.head;
head.insertBefore(langScript, head.firstElementChild);
By my understanding, these scripts should load and execute in the order eng.js
/fra.js
-> task*.js
-> inline, but this doesn't seem to be the case (at least in Chrome and Firefox). How should I modify my code such that it executes in the correct order? (I would prefer not to use callbacks if possible as I don't want to change the individual JS files too much.)
Upvotes: 4
Views: 1491
Reputation: 4320
import()
You could do something like the following:
<script>
elements for each of the task*.js
files from the documentload
event of the inserted language scripttask*.js
scripts inside that event listenerload
event of each task*.js
and use them to resolve a Promise
, which is combined to form a global Promise
Promise
in the inline script.Doing that, the relevant parts of lang.js
would become:
const langScript = document.createElement('script');
langScript.setAttribute('src', FR ? 'fra.js' : 'eng.js');
const fullyLoaded = new Promise(resolve => {
langScript.addEventListener('load', () => {
const taskPromises = [];
for (let i = 1; i < 5; i++) {
const script = document.createElement('script');
script.setAttribute('src', `task${i}.js`);
taskPromises.push(new Promise(resolve => {
script.addEventListener('load', resolve);
}));
head.insertBefore(script, head.firstElementChild);
}
resolve(Promise.all(taskPromises));
});
});
const head = document.head;
head.insertBefore(langScript, head.firstElementChild);
and the document would look something like:
<html>
<head>
<script src="lang.js"></script>
<script>
window.addEventListener('load', async () => {
await fullyLoaded;
console.log('start of inline');
});
</script>
</head>
</html>
None of the other scripts would need to be modified.
With this scheme:
lang.js
is loaded firsteng.js
/fra.js
is completely loaded secondtask1.js
through task4.js
are completely loaded in any orderYou will need to look at whether this manual deferral causes the loading to take to long; mocking this up locally has all the scripts loaded anywhere from 150ms to 450ms.
import()
Effectively the same as the above, but using the import()
function-like keyword, lang.js
becomes:
const src = FR ? './fra.js' : './eng.js';
const fullyLoaded = import(src).then(() => Promise.all([
import('./task1.js'),
import('./task2.js'),
import('./task3.js'),
import('./task4.js')
]));
There are some differences in how JavaScript code is run inside something that is import
ed like this. The big ones are the imposition of strict mode, and the isolation of context, so you will most likely need to store any global variables explicitly onto the window
variable, if you aren't already, for the eng.js
, fra.js
and task*.js
files.
Upvotes: 3