【问题标题】:Load and execute javascript code SYNCHRONOUSLY同步加载和执行javascript代码
【发布时间】:2011-08-29 19:27:36
【问题描述】:

有没有办法像同步 XMLHttpRequest 一样以同步方式加载和执行 javascript 文件?

我目前正在使用同步 XMLHttpRequest,然后对此进行 eval,但调试该代码非常困难......

感谢您的帮助!

更新

我现在试过了:

test.html

<html>
    <head>
        <script type="text/javascript">
            var s = document.createElement("script");
            s.setAttribute("src","script.js");
            document.head.appendChild(s);
            console.log("done");
        </script>
    </head>
    <body>
    </body>
</html>

script.js

console.log("Hi");

输出: 完毕 嗨

所以它没有同步执行。任何想法让“嗨”首先出现?

更新 2 其他例子

test.html(脚本标签内的代码)

var s = document.createElement("script");
s.setAttribute("src","script.js");
document.head.appendChild(s);
SayHi();

script.js

function SayHi(){
    console.log("hi");
}

输出:未捕获的 ReferenceError:SayHi 未定义

【问题讨论】:

  • 不确定我是否理解 - JavaScript is 会同步执行,除非在 script 标记中使用了 deferred 属性。你能举例说明你的意思吗?
  • 对不起,不是真的。当您通过代码动态加载 JS 脚本时,与将其包含在标记中(就像询问者所做的那样)不同 - 默认情况下,它总是异步加载。请参阅下面的其余线程。
  • document.write() 是同步插入脚本标签的唯一方法。
  • 这能回答你的问题吗? Dynamically loading JavaScript synchronously

标签: javascript load synchronous


【解决方案1】:

包含js文件时可以使用“defer”属性。

<script defer="defer" src="contact/js/frontend/form.js"></script>

【讨论】:

    【解决方案2】:

    您可以在它们之间同步异步操作。创建递归函数来表示循环并在上一个完成时调用下一个操作。以下函数使用类似的技术按顺序导入脚本。它等待一个脚本被加载,如果没有错误继续下一个。如果发生错误,它会使用错误事件调用回调函数,并且如果没有错误 - 在加载所有脚本后调用具有 null 的相同回调函数。它还会使用s.parentNode.removeChild(s) 自行清理。

    function importScripts(scripts, callback) {
        if (scripts.length === 0) {
            if (callback !== undefined) {
                callback(null);
            }
            return;
        }
        var i = 0, s, r, e, l;
        e = function(event) {
            s.parentNode.removeChild(s);
            if (callback !== undefined) {
                callback(event);
            }
        };
        l = function() {
            s.parentNode.removeChild(s);
            i++;
            if (i < scripts.length) {
                r();
                return;
            }
            if (callback !== undefined) {
                callback(null);
            }
        };
        r = function() {
            s = document.createElement("script");
            s.src = scripts[i];
            s.onerror = e;
            s.onload = l;
            document.head.appendChild(s);
        };
        r();
    }
    

    【讨论】:

      【解决方案3】:

      如果你使用这个:

      function loadScriptSync (src) {
          var s = document.createElement('script');
          s.src = src;
          s.type = "text/javascript";
          s.async = false;                                 // <-- this is important
          document.getElementsByTagName('head')[0].appendChild(s);
      }
      

      你可以做你想做的事(虽然被分割在一个额外的脚本文件中)

      test.html(脚本标签内的代码):

      loadScriptSync("script.js");
      loadScriptSync("sayhi.js"); // you have to put the invocation into another script file
      

      script.js

      function SayHi() {
           console.log("hi");
      }
      

      sayhi.js

      SayHi();
      

      【讨论】:

      • 使用这个仍然会出错:loadScriptSync("cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js"); $().jquery;
      • 不确定你在说什么关于单独的脚本,但在加载脚本之前,loadScriptSync 函数中没有任何地方阻塞。
      • 是否 s.async = false;其实什么都做。调试时,不会在对象上将其设置为 async=false
      • loadScriptSync 不会内联执行它。使用s.async = false,scipts 只是按顺序加载。所以以这种方式加载多个脚本可以保证它们按顺序执行。
      • 这只是加载 ASYC.. 不同步
      【解决方案4】:

      来自一个类似的问题(https://*.com/a/3292763/235179):

      <script type="text/javascript">
        document.write('<script type="text/javascript" src="other.js"><\/script>');
      </script>
      
      <script type="text/javascript">
        functionFromOther();
      </script>
      

      document.write'd 脚本调用的代码需要在它自己的&lt;script&gt; 中,或者它需要在window.onload() 事件中。

      【讨论】:

        【解决方案5】:

        DOM 准备好后加载的所有脚本都是异步加载的。浏览器同步加载它们的唯一原因是函数write可以输出一些东西。所以你可以使用脚本元素的onload回调来实现你想要的。

        var s = document.createElement("script");
        s.setAttribute("src","script.js");
        s.onload = function(){
            console.log('Done');
        }
        document.head.appendChild(s);
        

        另一种方法是通过 XHR 加载 js 文件并在脚本元素中设置代码:

        window.onload = function(){
            var req = new XMLHttpRequest();
            req.open('GET', "test.js", false);
            req.onreadystatechange = function(){
                if (req.readyState == 4) {
                    var s = document.createElement("script");
                    s.appendChild(document.createTextNode(req.responseText));
                    document.head.appendChild(s);
                }
            };
            req.send(null);
        }
        

        【讨论】:

        • 这不是同步的 ;)
        • 是的,但据我判断,没有其他解决方案。此外,您的问题是:“有什么想法让“嗨”首先出现吗?”,而不是“如何同步进行?”。
        • 那我非得用eval吗?就像问题的标题一样,它是同步的。
        • 使用eval的原因是什么?
        • 然后我可以让它同步。我可以使用同步 XMLHttpRequest 来加载代码,然后使用 eval 来执行它。这似乎是唯一的方法......
        【解决方案6】:

        您的脚本会同步执行

        如果放在一起,您的代码是:

        1. create script element
        2. set its attribute src
        3. set its attribute deferred
        4. display done...
        

        这第一部分停止执行并将其交给下一个脚本

        5. script executes and displays Hi
        

        一切都非常同步...在 Javascript 中,一些代码会完全执行,直到执行到最后一行或将执行交给内部系统(如 XHR 或计时器)。

        当人们想要准备一些部分以供以后执行时,他们会使用setTimeout 进行准备。即使超时时间比其余代码短,它也将花费它执行的时间。代码执行完毕后。示例:

        // some code
        setTimeout(function(){ alert("I'm second alert"); }, 1);
        longExecutionTask();
        alert("I'm the first alert");
        

        在上面的代码中,即使 setTimeout 设置为在 1 毫秒后执行,它也不会启动,直到它完成执行后的代码,并以显示 alert 框结束。你的情况也是如此。第一批代码必须在其他任何事情开始之前完成执行。

        为什么会出现异常(在示例 2 中)

        在我写完答案后,您添加了更多代码,所以这里有更多信息。

        添加脚本标签不会立即执行。当 HTML 解析器到达您添加的 SCRIPT 元素时,将发生脚本加载+执行。它将在此时加载它并评估/执行其内容。

        1. HTML 解析器开始解析您的文档
        2. HEAD 正在被解析,它的 SCRIPT 子标签被解析并执行。此执行向 BODY 标记添加了一个尚未解析的元素。
        3. 解析器移动到BODY 并解析其内容(新添加的SCRIPT 标签),然后加载脚本并执行其内容。

        SCRIPT 元素只有在你的页面被解析并且已经在浏览器中呈现后被添加时才会立即执行。在你的情况下,情况并非如此。第一个脚本立即执行,动态添加的脚本在解析到达时执行。

        【讨论】:

        • No... 控制台首先显示“完成”,然后显示“Hi”,因此在添加脚本标签时它没有执行;)这是我的问题。我希望能够在添加标签后直接访问标签内的代码,例如一个函数-
        • @FlashFan:查看我关于 javascript 代码执行过程的附加信息。它应该让你明白。
        • @Robert:但它不是同步的,这不是问题所在。我正在寻求一种无需使用 eval 即可立即执行代码的方法。有可能吗?
        • @FlashFan:在我的字典中,同步执行意味着程序/串行执行,当你确切地知道什么时候会执行时。另一方面,异步取决于外部因素,因为您无法确定何时执行某事以及以何种顺序执行。所以你的代码非常同步。
        • @RobertKoritnik,所以根据你的回答是否定的。
        最近更新 更多