同步加载和执行 javascript 代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6074833/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:07:58  来源:igfitidea点击:

Load and execute javascript code SYNCHRONOUSLY

javascriptloadsynchronous

提问by Van Coding

Is there a way to load and execute a javascript file in a synchronous way just like a synchronous XMLHttpRequest?

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

I'm currently using a sync XMLHttpRequest and then eval for this, but debugging that code is very difficult...

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

Thanks for your help!

谢谢你的帮助!

Update

更新

I tried this now:

我现在试过这个:

test.html

测试.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

脚本.js

console.log("Hi");

Output: done Hi

输出:完成嗨

So it was not executed synchronously. Any idea to make "Hi" appear first?

所以它不是同步执行的。有没有让“嗨”先出现的想法?

Update 2Other example

更新 2其他示例

test.html (code inside a script tag)

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

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

script.js

脚本.js

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

Output: Uncaught ReferenceError: SayHi is not defined

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

回答by heinob

If you use this:

如果你使用这个:

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);
}

You can do what you want (although divided up in an additional script file)

你可以做你想做的事(虽然分成了一个额外的脚本文件)

test.html(code inside a script tag):

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

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

script.js:

脚本.js

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

sayhi.js:

sayhi.js:

SayHi();

回答by bjornd

All scripts which are loaded after DOM is ready are loaded asynchronously. The only reason for browser to load them synchronously is function writewhich can output something. So you can use onload callback of the script element to achieve what you want.

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

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

Another way is to load js-file via XHR and set code inside the script element:

另一种方法是通过 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);
}

回答by Josh Johnson

From a similar question ( https://stackoverflow.com/a/3292763/235179):

从一个类似的问题(https://stackoverflow.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>

Either the code called from the document.write'd script needs to be in it's own <script>or it needs to be in the window.onload()event.

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

回答by Robert Koritnik

Your scripts do execute synchronously

您的脚本确实同步执行

your code if put together is:

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

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

this first part stops execution and hands it over to next script

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

5. script executes and displays Hi

Everything is very much synchronous... In Javascript some code is executed completely until it executes to the last line or hands execution over to internal systems (like XHR or timer).

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

When one would like to put prepare some parts to execute later on, they prepare it with setTimeout. Even if timeout is shorter than the rest of the code will take that's the time it will execute. After code has finished executing. Example:

当人们想要准备一些部分以供稍后执行时,他们使用setTimeout. 即使超时时间比其余代码短,这也是它执行的时间。代码执行完毕后。例子:

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

In the above code even if setTimeoutis set to execute after 1ms it won't start until the code after it finishes execution which ends with displaying an alertbox. The same happens in your case. The first batch of code has to finish executing before anything else can start.

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

Why you're getting exception (in example 2)

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

You've added some more code after I've written my answer so here goes some more info.

在我写完答案后,您添加了更多代码,因此这里提供更多信息。

Adding a script tag will not immediately execute it. Script loading+execution will happen when HTML parser will get to the SCRIPTelement you added. It will load it at that point and evaluate/execute its content.

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

  1. HTML parser starts parsing your document
  2. HEADis being parsed and its SCRIPTchild tag gets parsed and executed. This execution adds one more element to BODYtag that hasn't been parsed yet.
  3. Parser moves on to BODYand parses its content (the newly added SCRIPTtag) which then loads the script and executes its content.
  1. HTML 解析器开始解析您的文档
  2. HEAD正在被解析并且它的SCRIPT子标签被解析和执行。此执行向BODY标记中添加了一个尚未解析的元素。
  3. 解析器继续BODY分析其内容(新添加的SCRIPT标签),然后加载脚本并执行其内容。

SCRIPTelements get immediately executed only when you they're added after your page has been parsed and is already rendered in browser. In your case that is not the case. The first script executes immediately and the dynamically added one executes when parses gets to it.

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

回答by User0123456789

You can synchronize asynchronous operations among themself. Create recursive function to represent a loop and call the next operation when previous finish. The following function imports scripts in order with the similar technique. It waits a script to be loaded and if there is no error continues with the next. If an error occur it calls the callback function with the error event and if there is no error - calls the same callback function with null after all scripts are loaded. It also cleans after itself with s.parentNode.removeChild(s).

您可以在它们之间同步异步操作。创建递归函数来表示一个循环,并在上一个完成时调用下一个操作。以下函数使用类似的技术按顺序导入脚本。它等待一个脚本被加载,如果没有错误继续下一个。如果发生错误,它会调用带有错误事件的回调函数,如果没有错误 - 在加载所有脚本后使用 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();
}