我正在尝试使用JavaScript进行简单的计时器。一切都好,但setTimeout()只是立即起作用。我该如何解决?
var flag = 0;
function getTimerValue(){
var val = parseInt(document.getElementById('timer').value,10);
if (isNaN(val)) {
val = 0;
}
return val;
}
function setTimerValue(newvalue){
document.getElementById('timer').value = newvalue;
}
function runTimer(){
if (flag == 0){
flag = 1;
while (flag == 1){
x = getTimerValue()
if (x < 1){
setTimerValue(0);
flag = 0;
} else{
setTimeout(setTimerValue(x - 1), 1000);
}
}
}
}
答案 0 :(得分:2)
它无法正常工作,因为您正在调用setTimerValue
函数(这就是它立即执行的原因)。 setTimeout
方法需要一个函数引用,因此一个选项是将其更改为:
setTimeout(function () {
setTimerValue(x - 1);
}, 1000);
或者Paulpro points out,您也可以使用:
setTimeout(setTimerValue, 1000, x - 1);
还值得一提的是,您也可以使用.bind()
方法:
setTimeout(setTimerValue.bind(null, x - 1), 1000);
答案 1 :(得分:1)
你的代码错了。如果定时器值非零,则永远循环,不断对事件进行排队以将相同的初始计数器值减1(例如,反复更改7到6,但从不6到5),但因为调度代码永远不会完成,控件永远不会被传回JS事件循环,以允许它处理任何这些事件。
setTimeout
不只是“睡眠指定的时间,然后运行此功能”,它不会阻止;它只是安排稍后发生的呼叫,并立即返回。仅当事件循环控制调度事件时才处理调度的调用;由于你的代码永远循环,事件循环永远不会重新获得控制权。
你需要在JS中异步思考;一旦调用异步功能,就不能在回调中的任何地方依赖结果。尝试重构代码以异步方式执行后续工作,因此您实际上会看到更新的值:
function decrementTimerUntilZero() {
var x = getTimerValue();
if (x >= 1) {
setTimerValue(x - 1);
setTimeout(decrementTimerUntilZero, 1000); // Schedule another decrement
} else {
setTimerValue(0);
}
}
if (getTimerValue() > 0) {
setTimeout(decrementTimerUntilZero, 1000);
}
如果它是正数,您最初会将其计划为递减,然后每次递减时,如果该值尚未达到零,则计划新的递减。
另一种方法(可能会获得更一致的时序,避免事件循环调度延迟永远不会被弥补)将使用setInterval
:
var timerinterval = setInterval(function() {
var x = getTimerValue();
if (x >= 1) {
setTimerValue(x - 1);
} else {
setTimerValue(0);
clearInterval(timerinterval);
}
}, 1000);
在这两种情况下,当你的函数完成时,你基本上放弃了控制,让事件循环做更多的工作,相信你的回调将来可以恢复正常工作。 JavaScript没有sleep
函数的概念(按照设计;睡眠会阻止事件循环,直到它完成,冻结浏览器UI);这是间歇性地正常工作的唯一方法。
如果您想了解更多信息,建议您查看What do I do if I want a JavaScript version of sleep()?,了解setTimeout
如何运作,为什么它不仅仅是阻塞睡眠等等。