2012-03-19 93 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延遲應該允許任何事件在沒有任何問題的情況下跳入。你的循環代碼將不會被快速執行,但你只需要測試它並看看。