document.createElement(“script”) synchronously

前端 未结 11 2085
执念已碎
执念已碎 2020-11-22 12:56

Is it possible to call in a .js file synchronously and then use it immediately afterward?



        
相关标签:
11条回答
  • 2020-11-22 13:34

    Asynchronous programming is slightly more complicated because the consequence of making a request is encapsulated in a function instead of following the request statement. But the realtime behavior that the user experiences can be significantly better because they will not see a sluggish server or sluggish network cause the browser to act as though it had crashed. Synchronous programming is disrespectful and should not be employed in applications which are used by people.

    Douglas Crockford (YUI Blog)

    Alright, buckle your seats, because it's going to be a bumpy ride. More and more people ask about loading scripts dynamically via javascript, it seems to be a hot topic.

    The main reasons why this became so popular are:

    • client-side modularity
    • easier dependency management
    • error handling
    • performance advantages

    About modularity: it is obvious that managing client-side dependencies should be handled right on the client-side. If a certain object, module or library is needed we just ask for it and load it dynamically.

    Error handling: if a resource fails we still get the chance to block only the parts that depend on the affected script, or maybe even give it another try with some delay.

    Performance has become a competitive edge between websites, it is now a search ranking factor. What dynamic scripts can do is mimic asynchronous behavior as opposed to the default blocking way of how browsers handle scripts. Scripts block other resources, scripts block further parsing of the HTML document, scripts block the UI. Now with dynamic script tags and its cross-browser alternatives you can do real asynchronous requests, and execute dependent code only when they are available. Your scripts will load in-parallel even with other resources and the rendering will be flawless.

    The reason why some people stick to synchronous scripting is because they are used to it. They think it is the default way, it is the easier way, and some may even think it is the only way.

    But the only thing we should care about when this needs to be decided concerning an applications's design is the end-user experience. And in this area asynchronous cannot be beaten. The user gets immediate responses (or say promises), and a promise is always better than nothing. A blank screen scares people. Developers shouldn't be lazy to enhance perceived performance.

    And finally some words about the dirty side. What you should do in order to get it working across browsers:

    1. learn to think asynchronously
    2. organize your code to be modular
    3. organize your code to handle errors and edge cases well
    4. enhance progressively
    5. always take care of the right amount of feedback
    0 讨论(0)
  • 2020-11-22 13:35

    I had the following problem(s) with the existing answers to this question (and variations of this question on other stackoverflow threads):

    • None of the loaded code was debuggable
    • Many of the solutions required callbacks to know when loading was finished instead of truly blocking, meaning I would get execution errors from immediately calling loaded (ie loading) code.

    Or, slightly more accurately:

    • None of the loaded code was debuggable (except from the HTML script tag block, if and only if the solution added a script elements to the dom, and never ever as individual viewable scripts.) => Given how many scripts I have to load (and debug), this was unacceptable.
    • Solutions using 'onreadystatechange' or 'onload' events failed to block, which was a big problem since the code originally loaded dynamic scripts synchronously using 'require([filename, 'dojo/domReady']);' and I was stripping out dojo.

    My final solution, which loads the script before returning, AND has all scripts properly accessible in the debugger (for Chrome at least) is as follows:

    WARNING: The following code should PROBABLY be used only in 'development' mode. (For 'release' mode I recommend prepackaging and minification WITHOUT dynamic script loading, or at least without eval).

    //Code User TODO: you must create and set your own 'noEval' variable
    
    require = function require(inFileName)
    {
        var aRequest
            ,aScript
            ,aScriptSource
            ;
    
        //setup the full relative filename
        inFileName = 
            window.location.protocol + '//'
            + window.location.host + '/'
            + inFileName;
    
        //synchronously get the code
        aRequest = new XMLHttpRequest();
        aRequest.open('GET', inFileName, false);
        aRequest.send();
    
        //set the returned script text while adding special comment to auto include in debugger source listing:
        aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';
    
        if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
        {
            //create a dom element to hold the code
            aScript = document.createElement('script');
            aScript.type = 'text/javascript';
    
            //set the script tag text, including the debugger id at the end!!
            aScript.text = aScriptSource;
    
            //append the code to the dom
            document.getElementsByTagName('body')[0].appendChild(aScript);
        }
        else
        {
            eval(aScriptSource);
        }
    };
    
    0 讨论(0)
  • 2020-11-22 13:45

    Ironically, I have what you want, but want something closer to what you had.

    I am loading things in dynamically and asynchronously, but with an load callback like so (using dojo and xmlhtpprequest)

      dojo.xhrGet({
        url: 'getCode.php',
        handleAs: "javascript",
        content : {
        module : 'my.js'
      },
      load: function() {
        myFunc1('blarg');
      },
      error: function(errorMessage) {
        console.error(errorMessage);
      }
    });
    

    For a more detailed explanation, see here

    The problem is that somewhere along the line the code gets evaled, and if there's anything wrong with your code, the console.error(errorMessage); statement will indicate the line where eval() is, not the actual error. This is SUCH a big problem that I am actually trying to convert back to <script> statements (see here.

    0 讨论(0)
  • 2020-11-22 13:49
    function include(file){
    return new Promise(function(resolve, reject){
            var script = document.createElement('script');
            script.src = file;
            script.type ='text/javascript';
            script.defer = true;
            document.getElementsByTagName('head').item(0).appendChild(script);
    
            script.onload = function(){
            resolve()
            }
            script.onerror = function(){
              reject()
            }
          })
    
     /*I HAVE MODIFIED THIS TO  BE PROMISE-BASED 
       HOW TO USE THIS FUNCTION 
    
      include('js/somefile.js').then(function(){
      console.log('loaded');
      },function(){
      console.log('not loaded');
      })
      */
    }
    
    0 讨论(0)
  • 2020-11-22 13:52

    This is way late but for future reference to anyone who'd like to do this, you can use the following:

    function require(file,callback){
        var head=document.getElementsByTagName("head")[0];
        var script=document.createElement('script');
        script.src=file;
        script.type='text/javascript';
        //real browsers
        script.onload=callback;
        //Internet explorer
        script.onreadystatechange = function() {
            if (this.readyState == 'complete') {
                callback();
            }
        }
        head.appendChild(script);
    }
    

    I did a short blog post on it some time ago http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its-loaded/

    0 讨论(0)
  • 2020-11-22 13:54

    I am used to having multiple .js files on my web site that depend one on another. To load them and ensure that the dependencies are evaluated in the right order, I have written a function that loads all the files and then, once they are all received, eval() them. The main drawback is that since this does not work with CDN. For such libraries (e.g., jQuery) it is better to include them statically. Note that inserting script nodes in the HTML dynamically won't guarantee that scripts are evaluated in the right order, at least not in Chrome (this was the major reason for writing this function).

    function xhrs(reqs) {
      var requests = [] , count = [] , callback ;
    
      callback = function (r,c,i) {
        return function () {
          if  ( this.readyState == 4 ) {
            if (this.status != 200 ) {
              r[i]['resp']="" ;
            } 
            else {
              r[i]['resp']= this.responseText ;
            }
            c[0] = c[0] - 1 ;
            if ( c[0] == 0 ) {
              for ( var j = 0 ; j < r.length ; j++ ) {
                eval(r[j]['resp']) ;
              }
            }
          }
        }
      } ;
      if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) {
        requests.length = reqs.length ;
      }
      else {
        requests.length = 1 ;
        reqs = [].concat(reqs);
      }
      count[0] = requests.length ;
      for ( var i = 0 ; i < requests.length ; i++ ) {
        requests[i] = {} ;
        requests[i]['xhr'] = new XMLHttpRequest () ;
        requests[i]['xhr'].open('GET', reqs[i]) ;
        requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ;
        requests[i]['xhr'].send(null);
      }
    }
    

    I haven't figured out how to make references to the same value without creating an array (for count). Otherwise I think it is self-explanatory (when everything is loaded, eval() every file in the order given, otherwise just store the response).

    Usage example:

    xhrs( [
           root + '/global.js' ,
           window.location.href + 'config.js' ,
           root + '/js/lib/details.polyfill.min.js',
           root + '/js/scripts/address.js' ,
           root + '/js/scripts/tableofcontents.js' 
    ]) ;
    
    0 讨论(0)
提交回复
热议问题