所以我正在制作一个Javascript计时器应用程序。我正在家里的闲暇时间在PC上(在linux中)开发它,并且朋友也在Windows环境中测试它。这两个似乎都很好,但在工作中我使用mac并在那里测试它。我只是在Chrome中的单独选项卡中打开它,而我正在做我的事情,但我也用经过的时间更新标题,所以我可以看到它而无需切换标签。
在Mac上,我正在看计时器,它似乎非常不一致,但只有当我没有打开那个标签时......如果我正在看页面,请将其100%罚款,但当我打开另一个标签时,显然不会正常计算秒数。它似乎停止了大约2秒钟,然后有时会在下一秒钟通过两次滴答而赶上,有时它不会。无论哪种方式,我现在整天都在使用它,现在工作大约6个小时,而且时间只有大约4个小时。
Mac是相当新的(他们购买了它并且我将其拆箱并在不到6个月前自行设置)所以我非常确定它不会在系统资源或类似的东西上运行不足......它通常不会很慢以任何其他方式。
它是一个非常标准的javascript setInterval调用:setInterval(function,1000);
唯一似乎无法解决的操作系统或存在此问题的操作系统是OSX。有什么想法为什么?我应该使用setTimeout从头开始构建计时器吗? (我真的很想避免这种情况,因为我已经走了这条路并遇到了问题......)
谢谢!
答案 0 :(得分:2)
这是设计的。如果选项卡未对焦,则不需要处理器实时关注。
相反,您的计时器应该理想地“睡眠”。就个人而言,我使用requestAnimationFrame
编织计时器,以便只有当选项卡处于焦点时才会执行 - 这样可以让用户看到更新发生在他们眼前之前。
此外,对于任何类型的计时器,您都不应该使用setInterval(..., 1000)
并且期望完全 1000毫秒。相反,使用Date.now()
计算实际经过的时间,并相应地更新。
答案 1 :(得分:1)
不要只使用setInterval
制作计时器,而是每次{{1}时需要计算开始和结束之间的经过时间调用回调函数。
时间准确,因为它是从两次计算出来的。开始时间和X时间。我们减去两次,得到已经过的秒数。
即使您已选中并且性能标签的性能降低,我们仍然会记住原始时间,因此当您切换回选项卡时,我们可以相应地更新计时器。
setInterval
var timer = function(elem){
//Auto instantiate timer.
if(!(this instanceof timer)){
return new timer(elem);
}
//elem where results are shown.
this.elem = elem;
//Initial time set to null
this.init = null;
//Paused time set to null
this.paused = null;
//SetInterval code set to -1
this.si = -1;
//Offset set to 0 (used to calculate the resumed time after a pause)
this.offset = 0;
}
timer.prototype = {
start: function(){
//If the tiemr is running, no point in trying to start it.
if(this.si > -1 && this.init !== null){
alert("Can't restart, stop it first.");
return;
}
//Create start time.
this.init = new Date();
this.resume();
},
//Reset all values.
stop: function(){
this.init = null;
this.paused = null;
this.offset = 0;
this.pause();
this.si = -1;
},
pause: function(){
//Remember the paused time, so that we can calculate the offset when we resume.
this.paused = new Date();
//stopping the timer.
clearInterval(this.si);
this.si = -1;
},
resume: function(){
//if it's running we don't try to resume.
if(this.si > -1){
alert("Can't resume, pause/start it first.");
return;
}
//if there is a pause time, then we calculate the offset.
if(this.paused !== null){
this.offset = Math.floor(((new Date()).getTime() - this.paused.getTime())/1000) + this.offset;
this.paused = null;
}
//Create the timer.
this.si = setInterval(function(){
var tick = new Date();
var elapsed_time_in_seconds = (tick.getTime() - this.init.getTime())/1000;
this.print(Math.floor(elapsed_time_in_seconds-this.offset));
}.bind(this), 100);
},
print: function(elapsed){
this.elem.innerHTML = elapsed;
},
bindPause: function(elem){
elem.onclick = function(){
this.pause();
}.bind(this);
},
bindResume: function(elem){
elem.onclick = function(){
this.resume();
}.bind(this);
},
bindStart: function(elem){
elem.onclick = function(){
this.start();
}.bind(this);
},
bindStop: function(elem){
elem.onclick = function(){
this.stop();
}.bind(this);
}
}
var timer1 = timer(document.getElementById('time1'));
var timer2 = timer(document.getElementById('time2'));
var timer3 = timer(document.getElementById('time3'));
var timer4 = timer(document.getElementById('time4'));
timer1.bindPause(document.getElementById('pause1'));
timer2.bindPause(document.getElementById('pause2'));
timer3.bindPause(document.getElementById('pause3'));
timer4.bindPause(document.getElementById('pause4'));
timer1.bindResume(document.getElementById('resume1'));
timer2.bindResume(document.getElementById('resume2'));
timer3.bindResume(document.getElementById('resume3'));
timer4.bindResume(document.getElementById('resume4'));
timer1.bindStart(document.getElementById('start1'));
timer2.bindStart(document.getElementById('start2'));
timer3.bindStart(document.getElementById('start3'));
timer4.bindStart(document.getElementById('start4'));
timer1.bindStop(document.getElementById('stop1'));
timer2.bindStop(document.getElementById('stop2'));
timer3.bindStop(document.getElementById('stop3'));
timer4.bindStop(document.getElementById('stop4'));
timer1.start();
timer2.start();
timer3.start();
timer4.start();