Dynamically loading JavaScript synchronously

后端 未结 18 2280
小蘑菇
小蘑菇 2020-11-27 13:55

I\'m using the module pattern, one of the things I want to do is dynamically include an external JavaScript file, execute the file, and then use the functions/variables in t

18条回答
  •  一生所求
    2020-11-27 14:39

    I've had a similar task a few days earlier, and here's how I did it.
    This loader works both in file:// prefixes as well as in http:// and https://, and is cross-browser compatible.
    It however, cannot load specific classes or functions as modules from scripts; it will load the whole script altogether and make it available to the DOM.

    // Loads a script or an array of scripts (including stylesheets)
    // in their respective index order, synchronously.
    // By Sayanjyoti Das @https://stackoverflow.com/users/7189950/sayanjyoti-das
    var Loader={
        queue: [], // Scripts queued to be loaded synchronously
        loadJsCss: function(src, onl) {
            var ext=src.toLowerCase().substring(src.length-3, src.length);
            if(ext=='.js') {
                var scrNode=el('script', null, null, null);
                scrNode.type='text/javascript';
                scrNode.onload=function() {onl();};
                scrNode.src=src;
                document.body.appendChild(scrNode);
            }else if(ext=='css') {
                var cssNode=el('link', null, null, null);
                cssNode.rel='stylesheet';
                cssNode.type='text/css';
                cssNode.href=src;
                document.head.appendChild(cssNode);
                onl();
            }
        },
        add: function(data) {
            var ltype=(typeof data.src).toLowerCase();
    
            // Load a single script
            if(ltype=='string') {
                data.src=data.src;
                Loader.queue.splice(0, 1, data, Loader.queue[0]);
                Loader.next();
            }
            // Load an array of scripts
            else if(ltype=='object') {
                for(var i=data.src.length-1; i>=0; i--) {
                    Loader.queue.splice(0, 1, {
                        src: data.src[i],
                        onload: function() {
                            if(Loader.next()==false) {
                                data.onload();
                                return;
                            }
                            Loader.next();
                        }
                    }, Loader.queue[0]);
                }
                Loader.next();
            }
        },
        next: function() {
            if(Loader.queue.length!=0 && Loader.queue[0]) {
                var scr=Loader.queue[0];
    
                // Remove the script from the queue
                if(Loader.queue.length>1)
                    Loader.queue.splice(0, 2, Loader.queue[1]);
                else
                    Loader.queue=[];
    
                // Load the script
                Loader.loadJsCss(scr.src, scr.onload);
            }else return false;
        }
    };
    

    The above function is very powerful and elegant; it allows you to load a single script or an array of script synchronously (i.e, next script not loaded until previous script loading finished). Moreover, a loaded script may load more scripts, which defers the queue in the parent script.

    BTW, a script here means a JavaScript file or a CSS stylesheet.

    Here's how to use it:-

    // Load a single script
    Loader.add({
        src: 'test.js',
        onload: function() {
            alert('yay!');
        }
    });
    
    // Load multiple scripts
    Loader.add({
        src: ['test1.js', 'test2.js', 'mystyles.css', 'test3.js'],
        onload: function() {
            alert('all loaded!');
        }
    });
    

    Note that, the onload function in the Loader arguments is called when all of the scripts have loaded, not when one or a single script is loaded.

    You can also load more scripts in the scripts you loaded, such as in test.js, test1.js, etc. By doing this, you will defer the load of the next parent script and the queue in the child script will be prioritized.

    Hope it helps :-)

提交回复
热议问题