一种解决因setInterval阻塞导致手写的时钟时间展示不准的方法

业务场景中经常需要显示当前时间,如

修改完character_set_results 需要重启吗 setinterval不准确如何改造_vue.js


一般开始的想法是设置个setInterval每隔1000毫秒给开始时间加1000毫秒,但setInterval是有可能被其他很耗时的代码阻塞运行,导致时钟时间显示不准的,比如切到别的网站页面之类的,经常碰到显示时间比实际时间差了十几分钟以上的,这显然不能接受。

百度找了下解决办法,大多都是用setTimeout代替setInterval来解决,每次都算一下时间误差,这个方法比较复杂,我就想着能不能用简单粗暴的方法解决。

我的方法是,既然每次算误差都要调用new Date(),那不如一开始获取服务器真实时间时,记录一下打开页面时的时间戳new Date().getTime(),然后用setInterval每1000毫秒触发一次,计算当前时间戳与一开始记录下来的时间戳的差值,把这个差值加到一开始获取的服务器真实时间时间戳上即为现在的准确时间戳

这个算法的好处有
1.简单,不容易出错。比setTimeout那个方法简洁,那个方法追求真正的流逝时间。
2.本机时间是否准确不影响。比如做安卓的时钟直接调的本机时间,但本机时间是可能不准确的,还是需要请求个服务器时间的,我的这个方法与本机时间是否准确无关,只求个差值。

缺点:
1.不是准确的流逝时间,是setInterval以1000毫秒地间隔获取下当前时间戳,setInterval可能会被阻塞。
2.显示的时间也是抹了零的,因为每1000毫秒计算的时间差,不是满了整数毫秒来更新时间的,适用于显示不用那么准确的业务场景,比如显示个普通的时间,倒计时业务场景可能会有零点几秒的延时

获取服务器时间

// 获取服务器时间
var xhr = new XMLHttpRequest();
if(!xhr){
  xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open("HEAD", location.href, false);
xhr.send();
this.openTime = new Date(xhr.getResponseHeader("Date")).getTime(); // 储存打开页面时 服务器时间的时间戳格式

计算时间差算法

this.openTime = new Date(xhr.getResponseHeader("Date")).getTime(); // 储存打开页面时 服务器时间的时间戳格式
this.openTimestamp = new Date().getTime(); // 打开网页时的本地时间戳
this.presentDate = currDate('cdate', this.openTime);
this.presentTime = currDate('time', this.openTime);

this.timerInterval = setInterval(() => {
  let timeChange = new Date().getTime() - this.openTimestamp; // 计算时间差 避免因定时器阻塞造成时间计算错误
  this.presentTime = currDate('time', this.openTime + timeChange)
  // 每天凌晨12点 强制刷新页面
  if(this.presentTime.indexOf('00:00:00') > -1) {
    location.reload();
  }
}, 1000);

时间戳转换时间格式方法

export function currDate(type, time) {
  var date = time ? new Date(time) : new Date();
  var dateStr = date.toLocaleString('chinese', {hour12: false}).replace(/\//g, '-');
  var dateArr = dateStr.split(' ')
  switch(type) {
    case 'date':
      return dateArr[0];
    case 'time':
      return dateArr[1];
    case 'datetime':
      return dateStr;
    case 'cdate':
      var arr = dateArr[0].split('-');
      return arr[0] + '年' + arr[1] + '月' + arr[2] + '日'
    default:
      return dateStr;
  }
}