conditionally load javascript (external and internal) and keep execution order

后端 未结 1 1895
余生分开走
余生分开走 2021-01-20 07:41

I\'m looking for a way to conditionally load and keep the execution order of some javascript files (external and internal) without any library dependency. Basically, what I

相关标签:
1条回答
  • 2021-01-20 08:43

    Adding script nodes should work just fine. Because those scripts will execute asynchronously to the code adding them, you'll need to give them a callback to call to do the next thing in order. E.g.:

    if (window.localStorage) {
        // Load the local storage stuff; once loaded, it'll call
        // `doTheNextThing`
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = /* ... the URL of the script ... */;
        document.body.appendChild(script); // Or append it to `head`, doesn't matter
                                           // and `document.body` is convenient
    }
    else {
        // Skip loading it
        setTimeout(doTheNextThing, 10);
    }
    
    function doTheNextThing() {
        // ...
    }
    

    ...where the dynamic script you're loading for the localStorage stuff call doTheNextThing after it loads — so in the case where there's localStorage, the dynamically-loaded script calls doTheNextThing but in the case where there isn't, the code above does. Note that I made the call from the code above asynchronous (via setTimeout) on purpose: Making it always asynchronous regardless of how it gets called reduces your odds of missing bugs (e.g., adding something that relies on it being called synchronously and then forgetting to test that minor change on IE).

    Update: The above assumes you're in control of the script you're loading, but you've clarified that you're not. In that case, what you need to do is load the scripts one at a time and poll for the feature that they provide (usually a property on the window object, like window.jQuery), something like this (untested):

    // Load the script designated by `src`, poll for the appearance
    // of the symbol `name` on the `window` object. When it shows
    // up, call `callback`. Timeout if the timeout is reached.
    function loadAndWait(src, name, timeout, callback) {
        var stop, script;
    
        // Do nothing if the symbol is already defined
        if (window[name]) {
            setTimeout(function() {
                callback("preexisting");
            }, 10);
        }
        else {
            // Load the script
            script = document.createElement('script');
            script.type = "text/javascript";
            script.src = src;
            document.body.appendChild(script);
    
            // Remember when we should stop
            stop = new Date().getTime() + timeout;
    
            // Start polling, long-ish initial interval
            setTimeout(poll, 150);
        }
    
        function poll() {
             if (window[name]) {
                 // Got it
                 callback("loaded");
             }
             else if (new Date().getTime() > stop) {
                 // Time out
                 callback("timeout");
             }
             else {
                 // Keep waiting, shorter interval if desired
                 setTimeout(poll, 75);
             }
        }
    }
    

    ...which you'd use like this for the jQuery load:

    loadAndWait(
        "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
        "jQuery",
        10000, // ten seconds or whatever
        function(result) {
            // ...do the next one if result !== "timeout"
        }
     );
    

    You can either nest calls to loadAndWait in each of the previous calls' callbacks, or use an array and counter:

    loadThese(
        [
            {   src:    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
                symbol: "jQuery"
            },
            {
                src:     "http://the-next-one",
                symbol:  "nextSymbol"
            }
        ],
        doTheNextThing
    );
    
    function loadThese(scripts, callback) {
        var index = 0;
        run("okay");
    
        function run(result) {
            var entry;
    
            if (result === "timeout") {
                callback(result);
            }
            else if (index < scripts.length) {
                entry = scripts[index++];
                loadAndWait(entry.src, entry.symbol, 10000, run);
            }
            else {
                callback("loaded");
            }
        }
    }
    

    There, loadThese sets up a loop using run to load each script in turn.

    All of the above is completely off-the-cuff and can probably be tightened and bullet-proofed, but you get the idea.

    Off-topic, but my question is: Is there really so much code that it's a problem for the browsers that can't use it to load it? Barring the files getting a lot bigger, you'll actually slow down your site for users with advanced browsers without gaining much of anything on the others. Below a certain size, the overhead of connecting to the server to retrieve the script is as big a factor as transferring it. Is the extra stuff 50k of code? I'd do some benchmarking to test whether it's really necessary... Perhaps it is (perhaps you already have!), but it's worth just mentioning...

    Off-topic update: In your updated question, you list five separate scripts you'd be downloading if localStorage is supported. Even assuming you're getting all five from various CDNs, that's a lot of individual script requests (whether done in the usual way or as above), each of which has to be processed one at a time. That's a page load performance issue waiting to happen. Despite (possibly) losing the benefits of CDNs and existing caching, you might look at grabbing all of those scripts, combining them, and hosting your combined version in a single file. See "Minimize HTTP Requests" in the YUI performance "rules" (I prefer the term "guideline", but whatever). It would also simplify your dynamic loading.

    0 讨论(0)
提交回复
热议问题