I have a typescript application that dynamically adds script tags that point to JS files. Due to some restrictions I cant have these script tags statically defined in a html file, so I add them dynamically through typescript like this:
for (let src of jsFiles) {
let sTag = document.createElement('script');
sTag.type = 'text/javascript';
sTag.src = src;
sTag.defer = true;
document.body.appendChild(script);
}
Now, I am noticing that, when I add script tags dynamically, their doesn't seem to be a guarantee in the order in which they are loaded. Unfortunately, the jsFiles
array has scripts that are dependent on each other. So, the 2nd script in the array can only be loaded after the first one is fully loaded. The second script references a function that is defined in the first one. Is there a way I can specify the order in which scripts are ordered and executed when adding them dynamically (similar to how ordering is done when you statically define script tags in a html file)?
P.S. I would like to avoid using the onload callback to solve this issue since I noticed a performance degradation with my application. My second script file is very large and I am assuming that caused the degradation.
If you want the files ordered ... you need to wait for onload on each file. No way around it.
Here is a utility function I wrote once:
There are some ways to overcome that requirement. For now I can suggest the followings:
script
tag programmatically and set the attributeasync = false
.script
tag programmatically and set the callbackonload
in order to react when the script has been loaded asynchronously. The attributeasync = true
is set by default. (See example below)object
/array
that keep track of the scripts loaded.string
with the scripts in the required order (wrap each text-script inside an IIFE) and finally execute the text-script through the horrible and dangerouseval()
.setInterval
to check whether the script was executed. I added this last option only because I have seen some solutions using this bad technique.If you are working in a big project, I would recommend the first option. But if you have a small project, you can opt for the third option. Why?
Let's consider that in the second option, by setting the attribute
async = false
will cause the browser blocking rendering until the script has been executed (bad practice).I want to recommend a reading about script loaders: Deep dive into the murky waters of script loading, half hour worth spending!
I wrote an example of a small module to manage scripts injection, and this is the basic idea behind:
In this plunker you can test the injection of scripts asynchronously in a specific order:
The main idea was to create an API that allows you interact with the scripts to inject, by exposing the following methods:
addScript
: receive the url or anArray
of url's of the scripts.load
: Runs the task for load scripts in the specific order.reset
: Clear the array of scripts, or cancels the load of scripts.afterLoad
: Callback executed after every script has been loaded.onComplete
: Callback executed after all scripts have been loaded.I like the Fluent Interface or method chaining way, then I built the module that way:
In the code above, we load first the
"script1.js"
file, and execute theafterLoad()
callback, next, do the same with"script2.js"
and"script3.js"
and after all scripts were loaded, theonComplete()
callback is executed.