思考核心问题
倒计时时间的获取
客户端
- 当前时间获取客户端本地的时间可能会造成多次抢购的问题
- 原因:客户端本地时间是可以自己任意修改的 服务器端
- 时间必须从服务器端获取服务器的时间
- 弊端:会存在时间偏差的问题
- 优化:
- 使用 HEAD 方式请求:只获取服务器的响应头信息,不需要获取响应主体信息
- 当我们监听到 AJAX 请求转状态为 2 时就可以了,服务器返回时间直接在响应头信息中获取
每隔一秒后,时间的获取
- 在页面不刷新的情况下,每间隔 1 秒,不在从服务器获取时间,如果从服务器获取,时间误差会越来越大,服务器也会崩溃
- 所以,我们可以基于第一次获取的结果,手动累加 1000ms 即可
// 倒计时抢购
let countdownModule = (function () {
let textBox = document.querySelector('.text'),
serverTime = 0,
targetTime = +new Date('2020/12/05 16:00:00'),
timer = null;
// 获取服务器时间
const queryServerTime = function queryServerTime() {
return new Promise(resolve => {
let xhr = new XMLHttpRequest;
xhr.open('HEAD', '/');
xhr.onreadystatechange = () => {
if ((xhr.status >= 200 && xhr.status < 300) && xhr.readyState === 2) {
let time = xhr.getResponseHeader('Date');
// 获取的时间是格林尼治时间 -> 变为北京时间
resolve(+new Date(time));
}
};
xhr.send(null);
});
};
// 倒计时计算
const supplyZero = function supplyZero(val) {
val = +val || 0;
return val < 10 ? `0${val}` : val;
};
const computed = function computed() {
let diff = targetTime - serverTime,
hours = 0,
minutes = 0,
seconds = 0;
// 如果小于 0,那么抢购时间开始
if (diff <= 0) {
// 到达抢购时间了
textBox.innerHTML = '00:00:00';
// 清除定时器
clearInterval(timer);
return;
}
// 没到时间则计算即可
// 剩余时间中先计算出小时的毫秒数,然后在转换成小时
hours = Math.floor(diff / (1000 * 60 * 60));
// 用剩余毫秒数-计算出的小时的毫秒数,剩下的就是分钟毫秒数
diff = diff - hours * 1000 * 60 * 60;
// 分钟毫秒数转换为分钟
minutes = Math.floor(diff / (1000 * 60));
// 剩下的毫秒数 - 完整的分钟所占用的毫秒数,剩下的就是秒的毫秒数
diff = diff - minutes * 1000 * 60;
// 秒的毫秒数转换成秒
seconds = Math.floor(diff / 1000);
// 页面显示
textBox.innerHTML = `${supplyZero(hours)}:${supplyZero(minutes)}:${supplyZero(seconds)}`;
};
return {
async init() {
serverTime = await queryServerTime();
computed();
// 设置定时器
timer = setInterval(() => {
serverTime += 1000;
computed();
}, 1000);
}
};
})();
countdownModule.init();