The answer from Ciro Santilli is excellent. I have improved it a little bit because I faced some issues.
I made a special form field based on CodeMirror editor. Since the script for the CodeMirror is huge, I load it only on demand by field initialization.
If the form has 2 such fields, the loading of the script is started twice merely at the same moment, since the first started script is not loaded yet, it is actually loaded twice, what is bad.
The functions MyNamespace.loadScript(script_url) and MyNamespace.loadScripts(script_urls) always return a Promise so that you can decide whether to use await or then.
- If the script is already loaded, it returns a Promise that resolves immediately.
- If the script is being loaded, it returns a Promise with interval observing that resolves if the script is loaded.
- If the script was never loaded, it returns a Promise with loading script that resolves by loaded event.
The code:
MyNamespace.loadScript = function (script_url) {
console.log("loading: " + script_url);
if (!MyNamespace.loadedScripts) MyNamespace.loadedScripts = new Set();
if (!MyNamespace.loadingScripts) MyNamespace.loadingScripts = new Set();
if (MyNamespace.loadedScripts.has(script_url)) {
return new Promise(function (resolve, reject) {
console.log("already loaded");
resolve();
});
}
if (MyNamespace.loadingScripts.has(script_url)) {
return new Promise(function (resolve, reject) {
console.log("loading in progress");
let interval = setInterval(function () {
if (MyNamespace.loadedScripts.has(script_url)) {
clearInterval(interval);
console.log("waited until loaded");
resolve();
}
}, 200);
});
}
MyNamespace.loadingScripts.add(script_url);
return new Promise(function (resolve, reject) {
let script = document.createElement('script');
script.src = script_url;
script.onload = function () {
console.log("actually loaded");
MyNamespace.loadedScripts.add(script_url);
resolve();
};
script.onerror = function () {
console.log("load error");
reject(new Error("Error by loading the script " + script_url));
};
document.head.append(script);
});
};
MyNamespace.loadScripts = function (script_urls) {
let promises = [];
for (let script_url of script_urls) {
promises.push(MyNamespace.loadScript(script_url));
}
return Promise.all(promises);
};