同步加载和执行javascript代码

时间:2022-03-13 21:03:18

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,然后使用eval,但调试该代码非常困难......

Thanks for your help!

谢谢你的帮助!

Update

I tried this now:

我现在试过这个:

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

Output: done Hi

输出:完成嗨

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

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

Update 2 Other 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

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

Output: Uncaught ReferenceError: SayHi is not defined

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

5 个解决方案

#1


19  

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:

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

sayhi.js:

SayHi();

#2


12  

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

在DOM准备就绪后加载的所有脚本都是异步加载的。浏览器同步加载它们的唯一原因是函数写入可以输出一些东西。所以你可以使用脚本元素的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);
}

#3


10  

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

从类似的问题(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>

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脚本调用的代码需要在它自己的

#4


4  

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 setTimeout is set to execute after 1ms it won't start until the code after it finishes execution which ends with displaying an alert box. The same happens in your case. The first batch of code has to finish executing before anything else can start.

在上面的代码中,即使setTimeout设置为在1ms之后执行,它也不会启动,直到代码完成执行后才显示警告框。你的情况也是如此。第一批代码必须先完成执行才能开始其他任何操作。

Why you're getting exception (in example 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 SCRIPT element you added. It will load it at that point and evaluate/execute its content.

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

  1. HTML parser starts parsing your document
  2. HTML解析器开始解析您的文档

  3. HEAD is being parsed and its SCRIPT child tag gets parsed and executed. This execution adds one more element to BODY tag that hasn't been parsed yet.
  4. 正在解析HEAD并解析并执行其SCRIPT子标记。此执行向BODY标记添加了一个尚未解析的元素。

  5. Parser moves on to BODY and parses its content (the newly added SCRIPT tag) which then loads the script and executes its content.
  6. 解析器移动到BODY并解析其内容(新添加的SCRIPT标记),然后加载脚本并执行其内容。

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

#5


0  

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).

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

function importScripts(scripts, callback) {
    if (scripts.length === 0) {
        if (callback !== undefined) {
            callback(null);
        }
    }
    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();
}

#1


19  

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:

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

sayhi.js:

SayHi();

#2


12  

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

在DOM准备就绪后加载的所有脚本都是异步加载的。浏览器同步加载它们的唯一原因是函数写入可以输出一些东西。所以你可以使用脚本元素的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);
}

#3


10  

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

从类似的问题(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>

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脚本调用的代码需要在它自己的

#4


4  

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 setTimeout is set to execute after 1ms it won't start until the code after it finishes execution which ends with displaying an alert box. The same happens in your case. The first batch of code has to finish executing before anything else can start.

在上面的代码中,即使setTimeout设置为在1ms之后执行,它也不会启动,直到代码完成执行后才显示警告框。你的情况也是如此。第一批代码必须先完成执行才能开始其他任何操作。

Why you're getting exception (in example 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 SCRIPT element you added. It will load it at that point and evaluate/execute its content.

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

  1. HTML parser starts parsing your document
  2. HTML解析器开始解析您的文档

  3. HEAD is being parsed and its SCRIPT child tag gets parsed and executed. This execution adds one more element to BODY tag that hasn't been parsed yet.
  4. 正在解析HEAD并解析并执行其SCRIPT子标记。此执行向BODY标记添加了一个尚未解析的元素。

  5. Parser moves on to BODY and parses its content (the newly added SCRIPT tag) which then loads the script and executes its content.
  6. 解析器移动到BODY并解析其内容(新添加的SCRIPT标记),然后加载脚本并执行其内容。

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

#5


0  

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).

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

function importScripts(scripts, callback) {
    if (scripts.length === 0) {
        if (callback !== undefined) {
            callback(null);
        }
    }
    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();
}