Asynchronous Module Pattern

The asynchronous module mode AMD is to continue other business logic after the request is issued until the module is loaded and the subsequent logic is executed to realize the reference to the module after the module is loaded in module development. The famous require.js is based on this idea Yes, the asynchronous module pattern does not belong to the generally defined 23 design patterns, but is usually regarded as an architectural design pattern in a broad sense.

Description

The asynchronous module mode is mainly used in the browser environment. The browser environment is different from the server environment. In order not to block the rendering thread, external Js files are usually loaded asynchronously, so some module methods in the file must be used. After going through the file loading process, the synchronous module mode cannot be applied to this problem, and the asynchronous module mode needs to be used. The asynchronous module mode not only reduces the problem of variables and method names being overwritten during multi-person development, but also increases module dependencies, so that developers do not have to worry about unusable problems caused by some methods not being loaded or not being loaded. Some functions are loaded asynchronously You can also strip out more unnecessary functions on the first screen to reduce the cost of loading the first screen.

Implementation

// dom.js
F.module("./dom", function() {
    return {
        g: function(id) {
            return document.getElementById(id);
        },
        html: function(id, html) {
            if (!html) return this.g(id).innerHTML;
            else this.g(id).innerHTML = html;
        }
    }
});
// event.js
F.module("./event", ["./dom"], function(dom) {
    return {
        on: function(id, type, fn) {
            dom.g(id)["on" + type] = fn;
        }
    }
});
<!-- 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>

Daily Question

https://github.com/WindrunnerMax/EveryDay

Reference

https://www.jianshu.com/p/d71fc902051e https://blog.csdn.net/WuLex/article/details/107350493 https://blog.csdn.net/a545415/article/details/77930534