没有明显原因的无限循环

Infinite loop in while for no aparent reason

我正在开发一个计时器,在我在这个论坛上得到一些答案后,它一直运行得很顺利。这就是它现在的样子:(只是为了让你明白)

我的代码(请注意,这是一项正在进行的工作,所以有些东西还没有完全完成;基本上每个具有 alert("workin"); 的函数仍然没有被使用。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>

<html>

<head>

<title>WIP</title>
<meta charset="UFT-8">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>


<script>

$(document).ready(function() {
 timerSet(9,12);
 timerRun();
});

function timerReset() {
 alert("workin");
}

function timerSet(inputMinutes, inputSeconds) {
 minutes = inputMinutes; 
 seconds = inputSeconds; 
 finalTimeInSeconds = minutes*60 + seconds; //finalTimeInSeconds is the time it takes for the timer to be 00:00 in seconds.
 timerPrint();
}

function timerAdd(inputMinutes, inputSeconds) {
 alert("workin");
}

function timerSubtract(inputMinutes, inputSeconds) {
 setTimeout(function () {
  if(minutes > 0 && seconds == 0) {
   minutes--;
   seconds = 59;
  } else {
   seconds--;
  }
  timerPrint();
 }, 1000);
}

function timerRun() {
 timerSubtract();
}

function timerStop() {
 alert("workin");
}

function timerPrint() {
 displayMinutes = (minutes.toString().length == 2) ? minutes : "0" + minutes; //ternary operator: adds a zero to the beggining 
 displaySeconds = (seconds.toString().length == 2) ? seconds : "0" + seconds; //of the number if it has only one caracter.
 $("#timerText").text(displayMinutes + ":" + displaySeconds);
}

function totalTime() {
 var totalTimeInSeconds = minutes*60 + seconds;

 return totalTimeInSeconds; //totalTimeInSeconds is the time that the timer now displays in seconds.
}

</script>

</head>

<body>

<div id="timerText">00:00</div>

</body>

</html>

所以这是我的问题:在 timerRun() 函数中,我希望 timerSubtract() 函数重复 while totalTime() > 0,但是如果我使用 while 循环,页面就会崩溃。为什么要这样做?我不认为这是一个无限循环。我可以做些什么来做我想做的事?

感谢回答者! :-)

问题是 timerSubtract 使用 setTimeout 可以正常工作。

每次使用 setTimeout 时,您都可以想象它正在为以后保留该功能。然后,一旦程序没有做任何事情(并且如果已经过了足够的时间),那么它 运行 就是下一个被搁置的函数。通过 运行 一个 while 循环,你永远不会给 运行 时间一个机会 运行 使用 setTimeout.

搁置的函数

在仍然使用 setTimeout 的情况下解决此问题的一种方法是执行以下操作:

function timerRun() {
    if (minutes > 0 && seconds == 0) {
        minutes--;
        seconds = 59;
    } else {
        seconds--;
    }
    timerPrint();
    // Queue up timerRun to run again in 1 second
    setTimeout(timerRun, 1000);
}
timerRun();

为什么不使用 setInterval 而不是使用 setTimeout:

http://jsfiddle.net/ouL1yzpq/

setInterval(function () {
    if(minutes > 0 && seconds == 0) {
        minutes--;
        seconds = 59;
    } else {
        seconds--;
    }
    timerPrint();
}, 1000);

检查这个以了解如何停止计时器:how to stop "setInterval"

timerSubtract 函数不会更改变量 minutesseconds。它只会启动一个超时,稍后会更改变量。

如果您 运行 循环中的函数等待变量发生变化,那么变量将永远不会发生任何变化。 JavaScript 是单线程的,因此您必须退出该函数,并且 return 将控件交给浏览器以执行超时。当循环处于 运行 时,将不会处理超时事件。

如果 JavaScript 是多线程的,那么您的循环将开始数百万次超时,直到一秒钟过去并且超时将开始执行。由于您不是每秒开始一次超时,而是尽可能快地开始,计时器会立即倒计时到负百万。

与其启动大量超时,不如在 timerSubtract 中使用单个 setInterval,并且只调用函数一次。间隔将每秒倒计时一次,因为我怀疑这就是您希望计时器工作的方式:

function timerSubtract(inputMinutes, inputSeconds) {
    setInterval(function () {
        if(minutes > 0 && seconds == 0) {
            minutes--;
            seconds = 59;
        } else {
            seconds--;
        }
        timerPrint();
    }, 1000);
}

function timerRun() {
    timerSubtract();
}

正如 Jonathan 所说,您可以使用 setInterval。我会在他的回答中添加 clearInterval() 以在 0 处停止(这是你想要的吗?) 像这样:

function timerSubtract(inputMinutes, inputSeconds) {
    var countdown = setInterval(function () {
        if(minutes > 0 && seconds == 0) {
            minutes--;
            seconds = 59;
        } else {
            seconds--;
        }

        if( totalTime() <= 0){
            clearInterval(countdown);
            // timer is done, do something interesting..
        }

        timerPrint();
    }, 1000);
}

此外,时间长了计时器可能会不准确。如果您想要更高的准确性,请在启动计时器时读取实际的当前系统时间,然后在每个时间间隔再次读取实际时间并减去两个值(当前 - 开始)以获得实际经过的时间。然后从初始总计中减去经过的时间,得到当前剩余时间。