如果前面的脚本失败,则执行或不执行JavaScript片段

时间:2012-10-25 11:13:27

标签: javascript jquery javascript-events error-handling

我刚刚学到了一个关于javascript执行的重要事实,如果出现错误。在我开始对此做出结论之前,我最好先核实一下我是对的。

给定一个包含2个脚本的HTML页面:

<script src="script1.js" />
<script src="script2.js" />

SCRIPT1:

doSomething();

SCRIPT2:

doSomeOtherThing();

这有效地导致单个脚本作为一个单元处理:

doSomething();
doSomeOtherThing();

特别是,如果doSomething抛出错误,则执行被破坏。 'script2'永远不会被执行。

这是我的“第1课” - 有人可能会认为,因为它是一个单独包含的文件,所以它不受script1的影响。 但确实如此。 =&gt;请参阅下面的“延迟更新”


现在,如果我们按如下方式更改script2(假设我们在上面某处包含了jQuery):

$(document).ready({ doSomeOtherThing(); });

并将脚本放在script2:

之前
<script src="script2.js" />
<script src="script1.js" />

执行的顺序实际上仍然是“doSomeO((有时)'doSomeOtherThing()'后面的'doSomething()'。

然而,它以两个“单位”执行:

  • doSomething作为文档的java脚本
  • 的一部分提前执行 处理document.ready事件时执行
  • doSomeOtherThing

如果doSomeOtherThing引发异常,则会破坏第二个处理“单位”。

(我不使用术语thread,因为我认为所有脚本通常都是由同一个线程执行的,或者更确切地说,这可能取决于浏览器。)

所以,我的 Lession 2 :即使JavaScript错误可能阻止任何后续脚本执行,它也不会停止事件循环。


结论1

$(document).ready()在定义应该独立于任何其他脚本执行的JavaScript代码块方面做得非常出色。

或者,换句话说:如果您有一段JavaScript并且想要确保即使其他脚本失败也会执行它,请将其放在$(document).ready()内。

这对我来说是新的,因为如果脚本依赖于完全加载的文档,我只会使用该事件。


结论2

更进一步,将{em>所有脚本包装在$(document).ready()中以确保所有脚本都“排队”执行可能是一个很好的架构决策。在上面的第二个示例中,如果<{em>} script2.js之后包含{em},如示例1所示:

script1.js

script1.js中的错误会阻止<script src="script1.js" /> <script src="script2.js" /> 被注册,因为doSomeOtherThing()函数不会被执行。

但是,如果script1.js也使用了$(document).ready(),那也不会发生:

$(document).ready()

两条线都将被执行。然后,事件循环将执行$(document).ready(function() { doSomething(); }); $(document).ready(function() { doSomeOtherThing(); }); ,这将会中断,但doSomething不会受到影响。

这样做的另一个原因是呈现页面的线程可以尽快返回,并且事件循环可用于触发代码执行。


批判/问题:

  • 我弄错了吗?
  • 有什么理由使得必须立即执行一段代码 ,即不将其包装到事件中?
  • 会对性能产生重大影响吗?
  • 是否有其他/更好的方法来实现相同而不是使用文档就绪事件?
  • 如果所有脚本只将其代码注册为事件处理程序,是否可以定义脚本的执行顺序?事件处理程序是按照它们的注册顺序执行的吗?

期待任何有用的评论!

延迟更新:

正如Briguy37正确指出的那样,我的观察一定是错误的。 (“我错了 - 是的!”)。以他的简单示例为例,我可以在所有主流浏览器中重现它,甚至在IE8中,即使script1抛出错误,也会执行script2。

仍然@ Marcello的伟大答案有助于深入了解执行堆栈的概念等。似乎两个脚本中的每一个都在一个单独的执行堆栈中执行。

3 个答案:

答案 0 :(得分:15)

JS处理错误的方式取决于JS处理脚本的方式。它与线程没有任何关系(或很少)。因此,您必须首先考虑JS如何通过您的代码。

首先,JS会逐步读取每个脚本文件/块(此时您的代码只是看作文本)。

比JS开始解释那些文本块并将它们编译成可执行代码。如果发现语法错误,JS将停止编译并继续执行下一个脚本。在这个过程中,JS将每个脚本块/文件作为分离的实体处理,这就是为什么脚本1中的语法错误不一定会破坏脚本2的执行。代码将被解释和编译,但此时不执行,因此{ {1}}命令不会破坏执行。

在编译完所有脚本文件/块之后,JS会遍历代码(从代码中的第一个代码文件/块开始)并构建一个所谓的执行堆栈(函数调用函数b调用函数d和c)。 ...)并按给定的顺序执行它。如果在任何时候发生处理错误或以编程方式抛出(throw new Error),则停止该堆栈的整个执行,并且JS返回到该堆栈的开头并开始执行下一个可能的堆栈

那就是说,你的onload函数在script1.js中发生错误之后仍然执行的原因,不是因为一个新的线程或其他东西而发生的,只是因为一个事件构建了一个单独的执行堆栈,JS可以跳转到,在上一个执行堆栈发生错误后发生。

回答你的问题:

  

有什么理由使得必须立即执行一段代码,即不将其包装到事件中?

我建议您在网络应用程序中根本没有“立即”调用代码。最佳实践是在应用程序中有一个入口点,在onload事件中调用

throw new Error('fail')

然而,这与错误处理等无关。错误处理本身绝对应该在代码内部使用条件$(document).ready(function () {App.init()}); 或try / catch / finally块进行,您可能会遇到潜在的错误。这也让你有机会在catch块中尝试第二种方法,或者最终正常处理错误。

如果您可以构建应用程序事件驱动(当然不是出于错误处理原因),请执行此操作。 JS是一种事件驱动的语言,在编写事件驱动的代码时,你可以充分利用它......

  

它会显着影响性能吗?

事件驱动的方法可以使您的应用程序表现更好,同时使其更加稳固。事件驱动的代码可以帮助您减少内部处理逻辑的数量,您只需要进入它。

  

是否有其他/更好的方法来实现相同而不是使用文档就绪事件?

如前所述:try / catch / finally

  

如果所有脚本只将其代码注册为事件处理程序,是否可以定义脚本的执行顺序?事件处理程序是按照它们的注册顺序执行的吗?

如果在同一对象上注册同一事件,则会保留该顺序。

答案 1 :(得分:8)

您首先假设它们作为一个脚本运行是不正确的。即使Script1抛出错误,Script2仍将执行。对于简单测试,请实现以下文件结构:

-anyFolder
--test.html
--test.js
--test2.js

test.html的内容:

<html>
   <head>
       <script type="text/javascript" src="test.js"></script>
       <script type="text/javascript" src="test2.js"></script>
   </head>
</html>

test.js的内容:

console.log('test before');
throw('foo');
console.log('test after');

test2.js的内容:

console.log('test 2');

打开test.html(在控制台中)时的输出:

test before test.js:1
Uncaught foo test.js:2
test 2 

从这个测试中,你可以看到test2.js仍在运行,即使test.js会抛出错误。但是,test.js在遇到错误后停止执行。

答案 2 :(得分:2)

我不确定语法错误,但您可以使用try {} catch(e) {}来捕获错误并保持其余代码运行。

不会直到最后

var json = '{"name:"John"'; // Notice the missing curly bracket }

// This probably will throw an error, if the string is buggy
var obj = JSON.parse(json);

alert('This will NOT run');

将直到最后

var json = '{"name:"John"'; // Notice the missing curly bracket }

// But like this you can catch errors
try {
  var obj = JSON.parse(json);
} catch (e) {
  // Do or don'
}

alert('This will run');

<强>更新

我只想说明如何确保在发生错误时执行其余代码。

  

有什么理由使得必须执行一部分   代码立即,即不将其包装到事件中?

性能。每个此类事件都会填满事件队列。并不是说它会伤害很多,但它只是没有必要......为什么以后做一些工作,如果它现在可以完成?例如 - 浏览器检测和填充。

  

它会显着影响性能吗?

如果你每秒多次这样做,那么是的。

  

是否有其他/更好的方法来实现相同而不是使用   文件准备活动?

是。见上面的例子。

  

如果只是所有脚本,都可以定义脚本的执行顺序   将他们的代码注册为事件处理程序?是事件处理程序   按照他们注册的顺序执行?

是的,我很确定它们是。