I am creating a loader. Since I use Firebase, I need to load the specific script files dynamically in the exact order. Here are the code in my HTML files. JsPack.js is a loader that will load specific scripts one at a time.
<script id="loader" src="./header/jsPack.js" resources="firebasejs;firebaseInit;firebaseRef;jQuery"></script>
And here is my current jsPack.js file. This works perfectly in the exact order. However, it cannot load only specific scripts, and just loads all scripts at once.
// Dynamically loading dependancies let firebasejs = {"url":"https://www.gstatic.com/firebasejs/4.3.1/firebase.js", "type": "text/javascript", "sync": false}; let firebaseInit = {"url":"./js/firebase_init.js", "type": "text/javascript", "sync": true}; let firebaseRef = {"url":"./js/firebase_ref.js", "type": "text/javascript", "sync": true}; let jQuery = {"url":"./js/jquery-3.2.1.js", "type": "text/javascript", "sync": true}; let bootStrap = {"url":"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js", "type": "text/javascript", "sync": false}; function loadingDependancies(target){ return new Promise((resolve, reject) => { try { let script = document.createElement("script"); script.type = target.type; script.async = target.async; script.src = target.url; document.body.appendChild(script); } catch (err) { reject(err) }; }); }; loadingDependancies(firebasejs) .then(loadingDependancies(firebaseInit)) .then(loadingDependancies(firebaseRef)) .then(loadingDependancies(jQuery)) .catch(err => {console.log(err)})
Now, I have changed it into something like this.
// Dynamically loading dependancies let resources = document.getElementById("loader").getAttribute("resources") let resourcesArr = resources.split(";"); // console.log(resourcesArr); let list = { // JS Resources List firebasejs: {"url":"https://www.gstatic.com/firebasejs/4.3.1/firebase.js", "type": "text/javascript", "location": "body"}, firebaseInit: {"url":"./js/firebase_init.js", "type": "text/javascript", "location": "body"}, firebaseRef: {"url":"./js/firebase_ref.js", "type": "text/javascript", "location": "body"}, jQuery: {"url":"./js/jquery-3.2.1.js", "type": "text/javascript", "location": "body"}, bootStrap: {"url":"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js", "type": "text/javascript", "location": "head"} // CSS Resources List }; function loadingDependancies(resourceName){ new Promise((resolve, reject) => { try { let resource = list[resourceName]; console.log(resourceName); let script = document.createElement("script"); let placement = document.getElementsByTagName(resource.location)[0]; script.type = resource.type; script.src = resource.url; placement.appendChild(script); console.log("Loaded: ", resource.url); } catch (err) { reject(err) }; }); }; // const loadEachDependancy = async () => { // for (resourceName of resourcesArr) { // const x = await loadingDependancies(resourceName) // // console.log(x); // } // } // loadEachDependancy(); function forEachDependancy(arr, f){ var i = 0; var nextPromise = function() { if (i >= arr.length) return; else { var newPromise = Promise.resolve(f(arr[i])); i++; return newPromise.then(nextPromise); } } return Promise.resolve().then(nextPromise); } forEachDependancy(resourcesArr, loadingDependancies);
It should have been working. However, all the scripts are loaded at once and the 2nd script cannot get the objects from the 1st. Right now, I do not know what is the problem with my codes. The page loads very fast , proving that those scripts are loaded at the same time. The 2nd script also returns error since it cannot get the objects from the 1st script. However, the console.log states that the scripts are loaded one at a time, which really confuses me.
Please respond if you know what is the problem with my codes. Thank you in advance.
Update: onload() with async works perfectly. Thank you very much. I did not expect we could put async inside script. With this I will not have to use Promise at all.
Advertisement
Answer
Use the script async attribute
Seems like the problem could be solved easily by setting the async attribute to false. This way the scripts load in order and Firebase works. If set to true then the scripts can load in unpredictable order based on size, location, etc. And Firebase may fail because firebase-app.js must always be loaded first. And in fact, you’ll see those errors if you set async = true in the snippet.
script.async = false;
Run the snippet to see the scripts load in order:
[ "https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-analytics.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-app-check.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-functions.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-storage.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-performance.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js", "https://www.gstatic.com/firebasejs/8.10.1/firebase-remote-config.js", "https://code.jquery.com/jquery-3.6.0.min.js", "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" ].forEach(src => { let script = document.createElement('script'); script.src = src; // if set true the scripts load in any order and Firebase may fail script.async = false; script.onload = console.log(src); document.body.append(script); });
Loading scripts...