I'm trying to figure out a way to structure a new framework for work capable of injecting plugins. The idea is to have each file be loaded asynchronously.
Here is how I would love to configure my plugins:
<script id="target_root" src="assets/js/target/target.js" async="true"></script>
<script>
var target = target || {};
target.cmd = target.cmd || [];
target.cmd.push(function () {
target.loadPlugins([
{"name": "root", "src": "assets/js/target/target.root.js"},
{"name": "krux", "src": "assets/js/target/target.krux.js"}
]).then(
target.init([
{
'plugin': 'root',
'opts': {
'foo': 'bar'
}
},
{
'plugin': 'krux',
'opts': {
'foo': 'bar'
}
}
])
)
});
</script>
As I'd be using inline functions (within the DOM) I thought of using a command queue which on load would invoke all pushed functions (a bit like the googletag cmd of DFP).
As stated before each plugin would be loaded asynchronously so the initialization of each of them should only start when all of them are loaded (hence the then() function).
Here you have my script:
var target = (function(root, w, d, c) {
var queueIndex = 0,
amountPluginsLoaded = 0,
pluginsLoaded = [];
root.cmd = {
'queue': root && root.cmd ? root.cmd : [],
'push': function(fn) {
this.queue.push(fn);
this.next();
},
'next': function() {
if (this.queue.length > 0) {
this.queue.shift()();
}
}
};
root.init = function(plugins) {
};
root.loadPlugins = function(plugins) {
var i = 0,
len = plugins.length;
for(; i < len; i++) {
_loadExternalJS(plugins[i]);
}
};
function _loadExternalJS(plugin) {
var scriptRoot = d.getElementById('target_root'),
scriptElement = d.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.setAttribute('async', 'true');
scriptElement.onload = function() {
amountPluginsLoaded++;
pluginsLoaded.push(plugin.name);
};
scriptElement.setAttribute('src', plugin.src);
scriptRoot.parentNode.insertBefore(scriptElement, scriptRoot.nextSibling);
}
function _initPlugin(plugin) {
}
for (; queueIndex < root.cmd.queue.length; queueIndex++) {
root.cmd.next();
}
}(target || {}, window, document, console));
Here you have the basic cmd functionality which would be overridden and the loading of each of the scripts.
What I can't seem to figure is how to fire up the then(). I suppose you'd keep track of it in the _loadExternalJS() in it's onload event (as you can see in the code). But Simply adding an if(amountPluginsLoaded === pluginsLoaded.length) { fire all inits } seems unproductive and not something that belongs in the function. this is why I'd love to implement some then() feature.
Any ideas/opinions?
You could use promise and promise.all to check all of them are loaded.
root.loadPlugins = function(plugins) {
var promiseArray = plugins.map(function(plugin){
return _loadExternalJS(plugin);
});
return Promise.all(promiseArray);
};
function _loadExternalJS(plugin) {
return new Promise((resolve, reject) => {
var scriptRoot = d.getElementById('target_root'),
scriptElement = d.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.setAttribute('async', 'true');
scriptElement.onload = function() {
amountPluginsLoaded++;
pluginsLoaded.push(plugin.name);
resolve(plugin.name);
};
scriptElement.setAttribute('src', plugin.src);
scriptRoot.parentNode.insertBefore(scriptElement, scriptRoot.nextSibling);
});
}
then
root.loadPlugins().then(function(){
//initialize plugins
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With