<!-- demo.html -->
<!DOCTYPE html>
<html>
<head>
<title>Asynchronous module pattern</title>
</head>
<body>
<div id="demo">Click Me</div>
</body>
<script type="text/javascript">
(function(F) {
const moduleCache = {};
function getUrl(moduleName) {
return String(moduleName).replace(/\.js$/g, "") + ".js"
}
function loadScript(src) {
let _script = document.createElement("script");
_script.type = "text/javascript";
_script.charset = "UTF-8";
_script.async = true;
_script.src = src;
document.body.appendChild(_script);
}
function setModule(moduleName, params, callback) {
let _module = null, fn = null;
if(moduleCache[moduleName]) {
_module = moduleCache[moduleName];
_module.status = "loaded";
_module.exports = callback ? callback.apply(_module, params) : null;
while (fn = _module.onload.shift()) {
fn(_module.exports)
}
} else {
callback && callback.apply(null, params);
}
}
function loadModule(moduleName, callback) {
let _module = "";
if (moduleCache[moduleName]) {
_module = moduleCache[moduleName];
if (_module.status === "loaded") {
// This is very important. loadModule must be asynchronous. A certain suggestion on effectiveJS states that you should never call asynchronous functions synchronously. This is very important.
setTimeout(callback(_module.exports), 0);
} else {
// Called when loading is complete
_module.onload.push(callback);
}
} else {
// first load
moduleCache[moduleName] = {
moduleName: moduleName,
status: "loading",
exports: null,
onload: [callback]
};
loadScript(getUrl(moduleName));
}
}
F.module = function(...args) {
// Get the module constructor (the last parameter member in the parameter array)
let callback = args.pop();
// Get the dependent module (next to the callback function parameters, and the data type is an array)
let deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [];
// The module url (module ID)
let url = args.length ? args.pop() : null;
// Dependency module sequence
let params = [];
// Statistics on the number of unloaded dependent modules
let depsCount = 0;
if(deps.length) {
deps.forEach((v ,i) => {
// Increase the number of unloaded dependent modules statistics
depsCount++;
// Load dependent modules asynchronously
loadModule(deps[i], function(mod) {
// The number of dependent modules added in the dependent module sequence will be reduced by one.
depsCount--;
params[i] = mod;
// If all dependent modules are loaded
if(depsCount === 0) {
// Align the module in the module cache and execute the constructor
setModule(url, params, callback);
}
});
})
} else { // No dependent modules, directly execute the callback function
// Align the module in the module cache and execute the constructor
setModule(url, [], callback);
}
}
})((() => window.F = ({}))());
F.module(["./event", "./dom"], function(events, dom) {
console.log(events, dom)
events.on("demo", "click", function() {
dom.html("demo", "success");
})
});
</script>
</html>