简体   繁体   English

setTimeout导致无限的while循环

[英]setTimeout is causing infinite while loop

This is my first time using a while loop in a practical application so please forgive my ignorance. 这是我第一次在实际应用中使用while循环,所以请原谅我的无知。

I am creating a webpage that demonstrates the costs of using a lightbulb over time. 我正在创建一个网页,演示随着时间的推移使用灯泡的成本。

At this stage I am trying to use a while loop to update and display the number of hours that have passed since the user clicked the light switch. 在此阶段,我正在尝试使用while循环来更新并显示自用户单击电灯开关以来经过的小时数。 (1 hour represents 1 second of realtime) (1小时代表实时的1秒)

When I set breakpoints on firebug, everything operates normally until I get to the setTimeout method within my while loop. 当我在Firebug上设置断点时,一切正常,直到我进入while循环中的setTimeout方法为止。 After it breaks at the setTimeout method and I click continue, it immediately breaks at the same place again, without actually executing anything else. 在setTimeout方法中断并单击继续后,它立即再次在同一位置中断,而没有实际执行任何其他操作。

When I don't set breakpoints, it freezes firefox and I have to stop script execution. 当我不设置断点时,它会冻结Firefox,并且我必须停止脚本执行。

I rechecked to make sure that I am using setTimeout properly. 我重新检查以确保我正确使用了setTimeout。 Now I'm not even sure where to check or what to search for because I don't understand whats going wrong. 现在,我什至不知道在哪里检查或搜索什么,因为我不知道出了什么问题。 Even just a hint of what I might check or research would be immensely helpful. 即使只是暗示我可能会检查或研究的内容,也将大有帮助。

I have tried to comment the code as verbosely as possible. 我试图尽可能冗长地注释代码。 I'll be happy to clarify something if needed. 如果需要,我很乐意澄清一些问题。

I would highly recommend taking a look at the jsfiddle: 我强烈建议您看一下jsfiddle:

JS FIDDLE JS菲德尔

But here is my code: 但是这是我的代码:

My JS 我的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)");
        };
    });
});

My HTML 我的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>
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 is an event based language. Javascript是一种基于事件的语言。 This means that codes doesn't run constantly. 这意味着代码不会持续运行。 It only runs when there is an event. 它仅在发生事件时运行。 By using a while loop you have basically frozen it - the javascript is constantly running inside that loop. 通过使用while循环,您基本上已经冻结了它-javascript在该循环中不断运行。 That's fine for languages like C, which have to have something running all the time. 对于像C这样必须一直运行的语言来说,这很好。

But it's a mistake for javascript. 但这是JavaScript的错误。 For javascript you have to code it to respond to an event, then stop. 对于javascript,您必须对其进行编码以响应事件,然后停止。 The setInterval constantly generates an event for you, running a function every xx milliseconds. setInterval会不断为您生成一个事件,每隔xx毫秒运行一次函数。

In between runs of the timer no code is running! 在计时器运行之间,没有代码在运行! This is important to remember. 要记住这一点很重要。

JavaScript uses an event loop. JavaScript使用事件循环。 Because your while loop never yields ( #lightswitch 's state will forever be on because the infinite loop locks the UI, preventing the user from turing it off), the event loop never runs again, and the callback you register with setTimeout never gets a chance to execute. 因为您的while循环永远不会产生( #lightswitchstate将永远处于on state on因为无限循环会锁定UI,从而阻止用户对其进行关闭操作),因此事件循环永远不会再次运行,并且您在setTimeout注册的回调也永远不会机会执行。

What you're really looking to do is update your counter with a setInterval function. 您真正要做的是使用setInterval函数更新计数器。

setInterval(updateNumbers, 1000);

Based on yngum's answer, 根据yngum的回答,

I think it's better like this: 我认为这样更好:

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

And then 接着

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

See it here: http://jsfiddle.net/tdS3A/24/ 在这里查看: http : //jsfiddle.net/tdS3A/24/

But if you want maximum precision, you should store new Date().getTime() , because doing setTimeout or setInterval of 1 second doesn't ensure you that it will be called each second... 但是,如果要最大精度,则应该存储new Date().getTime() ,因为将setTimeoutsetInterval为1秒并不能确保每秒都会调用它...

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

See it here: http://jsfiddle.net/tdS3A/26/ 在这里查看: http : //jsfiddle.net/tdS3A/26/

Even if you want the number with maximum precision because you want the calculations to be precise, maybe you want to round that value when you show it to the user. 即使您希望该数字具有最高的精度,因为您希望计算精确,也可能希望在向用户显示该值时将其舍入。 Then, use 然后,使用

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

Since setTimeout is generally preferred over setInterval, another solution is 由于通常比setInterval更喜欢setTimeout,因此另一种解决方案是

Change 更改

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

To

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

This will check ur lightswitch every second, and stop when it is off. 这将每秒检查一次您的电灯开关,并在其关闭时停止。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM