JS 中也可以通过 new Date() 获取时间,但是获取的是客户端时间,这个事件客户端可以任意修改。
所以在项目中,如果时间作为重要参考标准,需要使用服务器时间。
思路:
向服务器发送一个请求
通过服务器返回的响应头获取到服务器时间
对获取的时间进行处理
将时间保存为全局变量
每过 1s,这个全局时间累加 1s,然后根据这个全局时间进行倒计时,避免一直发送请求
关于 new Date()
new Date()
获取当前客户端本机时间(是标准的事件格式数据 => 对象)
new Date(TimeStr)
把指定的时间字符串转换为标准时间数据,时间字符串支持很多格式,例如:YYYY-MM-DD HH:MM:SS
、YYYY/MM/DD
两个 Date 实例相减得到的是时间相差的毫秒差
实现
解决时差
由于服务器端返回数据需要时间,所以客户端拿到返回的时间时,与现在的时间会存在一定误差,这个误差只能让其变小,无法彻底解决。
解决方案:
(1)在 Ajax 状态码为 2 的时候,即接受到响应头的时候,就可以从中获取信息,而不是等到更靠后的状态
(2)把请求方式直接设置为 head,只获取请求头信息即可,响应主体内容不需要
【特别的】即使我们向服务器发送一个不存在的请求地址,返回的是 404 状态码,但是响应头信息中都会存在服务器时间(不建议使用,产生 404 请求不友好)
解决持续请求
一个客户端每间隔 1 秒都会向服务器发送一个新的请求,如果访问用户增多,服务器压力就非常大,很容易瘫痪(负载均衡)
解决方案:
只在页面打开的时候,获取服务器时间
将获取的时间存储为全局变量
每一秒刷新的时候,都将其在原来的基础上一直累加,而不是重新从服务器获取最新时间
代码
let oTime = document.querySelector('.time'),
oTimeSpan = oTime.querySelector('span'),
autoTimer = null,
nowTime = null;
//=> 从服务器端获取时间
let queryTime = function queryTime() {
return new Promise(reslove => {
//=> 向服务器发请求,把结果存储起来
let xhr = new XMLHttpRequest();
xhr.open('head', 'date.json');
xhr.onreadystatechange = () => {
if (xhr.readyState === 2 && /^(2|3)\d{2}$/.test(xhr.status)) {
nowTime = new Date(xhr.getResponseHeader('date'));
reslove();
}
};
xhr.send(null);
});
}
// 计算时间
let computedTime = function computedTime() {
let tarTime = new Date('2018-09-21 22:00:00'),
diffTime = tarTime - nowTime;
if (diffTime >= 0) {
let hours = Math.floor(diffTime / 3600000);
diffTime = diffTime - hours * 3600000;
let minutes = Math.floor(diffTime / 60000);
diffTime = diffTime - minutes * 60000;
let seconds = Math.floor(diffTime / 1000);
hours < 10 ? hours = '0' + hours : null;
minutes < 10 ? minutes = '0' + minutes : null;
seconds < 10 ? seconds = '0' + seconds : null;
oTimeSpan.innerHTML = `${hours} : ${minutes} : ${seconds}`;
nowTime = new Date(nowTime.getTime() + 1000);
return;
}
oTimeSpan.innerHTML = '开始抢购!!';
clearInterval(autoTimer);
}
let promise = queryTime();
promise.then(computedTime);
autoTimer = setInterval(computedTime, 1000);