2012-08-29 279 views
0

這是我第一次在實際應用中使用while循環,所以請原諒我的無知。setTimeout導致無限while循環

我正在創建一個網頁,演示隨着時間的推移使用燈泡的成本。

在此階段,我嘗試使用while循環更新並顯示用戶單擊燈開關後經過的小時數。 (1小時代表實時1秒)

當我在螢火蟲上設置斷點時,一切正常運行,直到我在while循環中找到setTimeout方法。在setTimeout方法中斷之後,我單擊繼續,它會立即再次在同一個地方中斷,而不會執行其他任何事情。

當我沒有設置斷點時,它凍結了firefox,我不得不停止腳本執行。

我重新檢查,以確保我正確使用setTimeout。現在我甚至不知道在哪裏檢查或搜索什麼,因爲我不明白髮生了什麼問題。即使只是我可能檢查或研究的暗示,也會非常有幫助。

我試圖儘可能詳細地評論代碼。如果需要,我會很樂意澄清一些事情。

我會強烈建議在的jsfiddle考慮看看:

JS FIDDLE

但這裏是我的代碼:

我的JS

$(document).ready(function() { 
    //set image to default off position 
    $('#lightswitch').css("background-image", "url(http://www.austinlowery.com/graphics/offswitch.png)"); 

    // setup the lifetime hours of the lightbulb for later use 
    var lifetimeHours = 0; 
    // setup function to update the calculated lifetime hours number on the webpage to 
    // be called later 
    function updateLifetimeHoursHtml (lifetimeHours) { 
     $('#lifetimeHours').html(lifetimeHours); 
    } 
    // set up function to to send to setTimeout 
    function updateNumbers() { 
     // increment lifetimeHours by one 
     lifetimeHours = lifetimeHours++; 
     // call function to update the webpage with the new number result 
     updateLifetimeHoursHtml(lifetimeHours); 
    } 

    // When the lightswitch on the webpage is clicked, the user should see the 
    // lifetime hours update every second until the user clicks the switch again 
    // which will then display the off graphic and pause the updating of the lifetime 
    // hours 
    $('#lightswitch').click(function(){ 
     // if the lightswitch is off: 
     if ($('#lightswitch').attr('state') == 'off') { 
      // set switch to on 
      $('#lightswitch').attr('state', 'on'); 
      // update graphic to reflect state change 
      $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)"); 
      // start updating the lifetime hours number on the webpage 
      // while the #lightswitch div is in the on state: 
      while ($('#lightswitch').attr('state') == 'on'){ 
       //call update numbers every second 
       setTimeout('updateNumbers()', 1000); 
      } 
     // the lightswich was not in the off state so it must be on 
     }else{ 
      // change the state of the switch to off 
      $('#lightswitch').attr('state', 'off'); 
      // update graphic to reflect state change 
      $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)"); 
     }; 
    }); 
}); 

我的HTML

<div id="container"> 
    <div id="lightswitch" state="off">&nbsp;</div> 
     <span>After </span><span id="lifetimehours">0</span><span> lifetime hours:</span> 
     <br><br> 
     <span><b>You have spent:</b></span> 
     <br><br> 
     <span id="dollaramoutelectricity"></span><span> on electricty</span> 
     <br> 
     <span id="mainttime"></span><span> on maintenace</span> 
     <br> 
     <span id="dollaramountbulbs"></span><span> on replacement bulbs</span> 
     <br><br> 
     <span><b>You have:</b></span> 
     <br><br> 
     <span>Produced </span><span id="amountgreenhousegasses"></span><span> of greenhouse gasses</span> 
     <br> 
     <span>Sent </span><span id="amounttrash"></span><span> of trash to the dump</span> 
     <br> 
     <span>Used </span><span id="amountelectricty"></span><span> of electricity</span> 

</div> 
+0

'while(stuff){setTimeout(...)}' - 您通過這種方式設置無限超時 - 嘗試使用if。 – moonwave99

+5

有點偏離主題,但沒有必要這麼多評論。任何閱讀'$('#lightswitch')。attr('state','on');'的人都會知道你將開關設置爲'on',而沒有解釋它的註釋。他們只是混淆了代碼。 – peacemaker

+0

對'setTimeout()'的調用將不起作用 - 您只需傳遞函數的名稱,而不是該字符串。 (反正不會真的有效,但這是問題的一部分。) – Pointy

回答

3
var switchTimer; 

$('#lightswitch').click(function(){ 
    if ($('#lightswitch').attr('state') == 'off') { 
     $('#lightswitch').attr('state', 'on'); 
     switchTimer = setInterval(updateNumbers, 1000); 
    } else { 
     $('#lightswitch').attr('state', 'off'); 
     $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)"); 
     clearInterval(switchTimer); 
    }; 

Javascript是一種基於事件的語言。這意味着代碼不會持續運行。它只在發生事件時運行。通過使用while循環,您基本上已經凍結了它 - JavaScript在該循環內部不斷運行。對於像C這樣的語言來說,這很好,因爲它必須一直運行着某些東西。

但這是一個錯誤的JavaScript。對於JavaScript,您必須編寫代碼來響應事件,然後停止。 setInterval會不斷爲您生成一個事件,每xx毫秒運行一次函數。

在計時器之間運行沒有代碼正在運行!這點很重要。

+0

'setInterval()'調用不會像寫入那樣工作 - 它應該是' setInterval(updateNumbers,1000);' – Pointy

+0

@Pointy謝謝!我正在急着試圖得到答案,並沒有檢查的東西 – Ariel

+0

非常感謝,你不僅用一個例子解決了我的問題,還給了我關於javascript如何工作和將其與其他類型的語言進行比較。我不知道基於事件的範例或其他任何範例,既然你提到它,我就可以查看它並熟悉它。我最初也只是通過setTimeout和setInterval的定義進行了檢查,並沒有理解它們之間的區別。現在我明白了!哦,它實際上是clearInterval而不是cancelInterval。再次感謝您的幫助! – Austin

3

JavaScript使用事件循環。因爲你的while循環永遠不會產生(#lightswitchstate永遠是on,因爲無限循環鎖定用戶界面,阻止用戶關閉它),事件循環不會再次運行,並且你註冊的回調永遠不會有機會執行。

你真正想要做的是用setInterval函數更新你的計數器。

setInterval(updateNumbers, 1000); 
+0

感謝你的解釋,它幫助我更好地理解了Javascript是如何工作的! – Austin

0

由於setTimeout的通常優於setInterval的,另一種解決方案是

變化

while ($('#lightswitch').attr('state') == 'on'){ 
    //call update numbers every second 
    setTimeout('updateNumbers()', 1000); 

setTimeout(function() { 
    updateNumbers(); 
    if ($('#lightswitch').attr('state') == 'on') 
     setTimeout(arguments.callee, 1000); 
    }, 0); 

這將檢查烏爾電燈開關每一秒,並且停止,當它已關閉。

1

基於yngum的回答,

我覺得是這樣的美好:

if ($('#lightswitch').attr('state') == 'off') { 
    $('#lightswitch').attr('state', 'on'); 
    $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)"); 
    updateNumbers(); 
} 

然後

function updateNumbers() { 
    lifetimeHours++; 
    updateLifetimeHoursHtml(lifetimeHours); 
    if($('#lightswitch').attr('state') == 'on'){ 
     setTimeout(updateNumbers,1000); 
    } 
} 

見這裏:http://jsfiddle.net/tdS3A/24/

但是如果你想最大精度,你應該存儲new Date().getTime(),因爲在做setTimeout秒或1秒不向您保證,它會被稱爲每一秒的setInterval ...

function updateNumbers() { 
    // call function to update the webpage with the new number result 
    updateLifetimeHoursHtml(lifetimeHours+(new Date().getTime()-timerStart)/1000); 
    // call updateNumbers() again 
    if($('#lightswitch').attr('state') == 'on'){ 
     timer=setTimeout(updateNumbers,1000); 
    } 
} 
$('#lightswitch').click(function(){ 
    // if the lightswitch is off: 
    if ($('#lightswitch').attr('state') == 'off') { 
     // set switch to on 
     $('#lightswitch').attr('state', 'on'); 
     // update graphic to reflect state change 
     $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)"); 
     // update the lifetime hours number on the webpage 
     timerStart=new Date().getTime(); 
     timer=setTimeout(updateNumbers,1000); 
     // the lightswich was not in the off state so it must be on 
    }else{ 
     // change the state of the switch to off 
     lifetimeHours+=(new Date().getTime()-timerStart)/1000; 
     clearTimeout(timer); 
     $('#lightswitch').attr('state', 'off'); 
     // update graphic to reflect state change 
     $('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)"); 
    }; 
}); 

見這裏:http://jsfiddle.net/tdS3A/26/

即使你想以最高精確度的數量,因爲你想要的計算是精確的,也許你希望在向用戶顯示時舍入該值。然後,使用

function updateLifetimeHoursHtml (lifetimeHours) { 
    $('#lifetimehours').html(Math.round(lifetimeHours)); 
} 
+0

謝謝你的提高,我喜歡你的方法! – Austin