在Mac上setInterval不好?

时间:2017-03-14 15:24:23

标签: javascript macos timer

所以我正在制作一个Javascript计时器应用程序。我正在家里的闲暇时间在PC上(在linux中)开发它,并且朋友也在Windows环境中测试它。这两个似乎都很好,但在工作中我使用mac并在那里测试它。我只是在Chrome中的单独选项卡中打开它,而我正在做我的事情,但我也用经过的时间更新标题,所以我可以看到它而无需切换标签。

在Mac上,我正在看计时器,它似乎非常不一致,但只有当我没有打开那个标签时......如果我正在看页面,请将其100%罚款,但当我打开另一个标签时,显然不会正常计算秒数。它似乎停止了大约2秒钟,然后有时会在下一秒钟通过两次滴答而赶上,有时它不会。无论哪种方式,我现在整天都在使用它,现在工作大约6个小时,而且时间只有大约4个小时。

Mac是相当新的(他们购买了它并且我将其拆箱并在不到6个月前自行设置)所以我非常确定它不会在系统资源或类似的东西上运行不足......它通常不会很慢以任何其他方式。

它是一个非常标准的javascript setInterval调用:setInterval(function,1000);

唯一似乎无法解决的操作系统或存在此问题的操作系统是OSX。有什么想法为什么?我应该使用setTimeout从头开始构建计时器吗? (我真的很想避免这种情况,因为我已经走了这条路并遇到了问题......)

谢谢!

2 个答案:

答案 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();