2012-03-19 94 views
20

我一直认为,因为JavaScript是单线程的,所以我可以附加事件处理程序,而不用担心处理程序在执行代码时执行。令我惊讶的是,我发现他们可以。根据this answer,,当脚本仍在运行时,“无响应脚本”对话框可能会导致事件发生可以中断JavaScript执行流程吗?

我用下面的代码测试此:

<script> 
    function loop() { 
     var cond; 
     onblur = function (event) { 
      cond = false; 
     }; 
     cond = true; 
     while (cond) 
      ; 
     alert('loop exited!'); 
    } 
</script> 
<button onclick="loop()">loop()</button> 

(jsFiddle)

在Firefox 11.0,该函数打印点击继续后, “循环退出”。循环似乎暂停,并允许执行事件。这类似于临时更改目标线程的上下文的Unix signal,。但它更加危险,因为它允许改变外部状态。

这是一个错误?我是否应该不再依赖JavaScript的单流执行模型并确保我的所有脚本都可以重入?或者,尽管有大型浏览器允许这种情况发生,但它是不是值得追求的缺陷?

+2

我想真正的问题是:“标准要求什么。” – 2012-03-19 22:11:21

+1

我认为最好考虑一些内置行为('alert()'等)发生的“暂停”,因为解释器的形式会迫使当前正在运行的函数产生“yield”,然后,当“暂停”完成时,恢复执行。换句话说,认为在“暂停”正在进行的同时实际返回的功能,然后继续作为延续继续。这样,事件处理变得更加有意义,因为它就像普通的事件处理一样。 – Pointy 2012-03-19 22:18:04

+7

获取“无响应的脚本”提示并不是您应该设计的正常事情。你的应用程序坏了,用户从来没有得到这些对话框之一。你应该设计你的脚本,这样就不会出现“无响应的脚本”提示,然后你就不必担心重新进入。在我看来,一个主要的浏览器允许事件在该提示中触发时有点小错误,但我认为修复代码更重要,因此提示从不发生,这不是问题。 – jfriend00 2012-03-19 22:18:10

回答

2

所以是的,如果你创建一个无限循环,你会挂你的JavaScript。差异浏览器将以不同的方式处理。 Firefox 11为我抛出一个窗口,说你的脚本已经挂起。 Chrome现在正在为我旋转。

这应该证明这一点。从来不打电话alert()在FF11或Chrome 17

while(true){} 
alert("sucks"); 

http://jsfiddle.net/JCKRt/1/

你问到在JavaScript中同步语句。还有一些其他同步语句会阻止JavaScript的执行,如alert(),confirm(),同步ajax调用等。

你通常希望避免JavaScript中的任何同步事物!如果你想暂停一些事情,你可能需要重新考虑设计。 JavaScript是事件驱动的。你不需要它在一个while循环中旋转,因为没有什么在页面上将工作,包括任何事件,如点击等。

+0

相关:https://bugzilla.mozilla.org/show_bug.cgi?id=340345现在,Mozilla中至少有一个特殊情况可以插入计时器,而同步XHR正在进行中,但一般情况下不太清楚。 – ephemient 2012-03-20 03:33:52

+2

我看到“自己在脸上”编辑... – Timothy003 2012-03-20 03:59:12

+0

如果您需要旋转一个while循环 - 不阻止UI事件 - 您可以使用web worker:https://developer.mozilla。org/En/Using_web_workers#Performing_computations_in_the_background – 2012-03-20 04:08:07

0

我认为问题出现在你开始处理事件时。

与其使用while循环,不如考虑使用递归函数。

这是我对你的代码进行的一些修改,应该根据需要执行。

<head> 
    <script> 
    function loop(that) { 
     var cond; 
     that.onblur = function (event) { 
      cond = false; 
     }; 
     cond = true; 
     var loop = 0 
     var xy = function x(){ 
      if(cond == true){ 
       loop++;   
       setTimeout(function(){xy()},0); 
      } else { 
       alert('loop exited! - '+loop); 
       return true; 
      } 
     } 
     xy(); 
    } 
    </script> 
</head> 
<body> 
    <button onclick="this.focus(); loop(this)">loop()</button> 
</body> 

使用setTimeout()和0延迟应该允许任何事件在没有任何问题的情况下跳入。你的循环代码将不会被快速执行,但你只需要测试它并看看。