Countdown - precise and consistent server time

I. Background

        In the project, the countdown required to alert the user to make a corresponding operation, and has relatively high requirements for accuracy countdown. A direct access server is a page start time, the timer provided in the page [setInterval (function {...}, 1000);], the respective page was found but it is inconsistent from the beginning of the loading time, and the user can make the corresponding operation causes the timer stuck, resulting in greater error.

Second, cause analysis

     Roughly the causes of the case are as follows:

  1. Network communication takes a certain amount of time; ( temporarily did not expect the way to calculate )
  2. Page loading will take some time, and the load times are not the same in different browsers;
  3. JS is a single thread, and then executed when there are other actions will cause the timer stuck, and other operations after the end of the other, resulting in errors.

Countdown error model

 NOTE: Since this model does not consider user's operation causes the timer time error caused by obstruction.

 Two, JS achieve a more precise countdown

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>倒计时</title>
        <script>
            var startTime = new Date().getTime();    // 此时时间(用于计算线程占有,以及渲染引起的时间误差)
            var i = 0;
            while(i < 80000){    // 模拟页面加载(占用线程)
                ++ i;
            }
        </script>
        <style>
            .outerDiv {
                width: 880px;
                margin: 50px auto;
            }
            .countDown {
                text-align: center; 
                padding-top: 10%; 
                padding-bottom: 5%; 
                line-height: 80px; 
                font-size: 46px; 
                color: #333;
            }
            .countDown i { 
                color: #fff; 
                display: inline-block; 
                border-radius: 10px; 
                width: 68px;
                height: 80px;
                background: #ff9f09; 
                margin: 0 6px;
                padding-right: 10px;
            }
        </style>
    </head>
    <body>
        <div class="outerDiv">
            <div id="timeStrDiv" class="countDown">
			
            </div>
        </div>
    </body>
    <script>
        window.onload = function(){
            countDown();
        };
		
        var timer = null;
        var showStr = "";
        var showDiv = document.getElementById("timeStrDiv");
        function countDown(times){
            if (times == null || times == '') {
                times = 24321;    // 从服务器获取得倒计时(单位为毫秒)
                times -= (new Date().getTime() - startTime);    // 获取倒计时开始时间(减去页面加载时间)
            }
            if (timer != null) {
                clearTimeout(timer);
            }
			
            var interval = 1000;    // 设定倒计时标准间隔差
            startTime = new Date().getTime();    // 此时时间(用于计算线程占有,以及渲染引起的时间误差)
            var count = 0;    // 标记执行次数
			
            // 判断是否需要倒计时
            if (Math.floor(times / interval) > 0) {
                timer = setTimeout(countDownStart, interval);
            } else {
                showStr = "倒计时:<i>00</i>天<i>00</i>小时<i>00</i>分钟<i>00</i>秒";
                showDiv.innerHTML = showStr;
            }
			
            /** 真正的倒计时  */
            function countDownStart(){
                count++;
				
                var j = 0;
                while(j < (count * 8000000)){    // 模拟逻辑代码(占用线程)
                    ++ j;
                }
				
                showStr = formatTime("倒计时:<i>DD</i>天<i>HH</i>小时<i>MM</i>分钟<i>ss</i>秒", times);
                showDiv.innerHTML = showStr;
				
                var offset = new Date().getTime() - (startTime + count * interval);   // 计算误差
                if (offset > interval) {    // 若误差超过间隔时间,则立即校正(会出现跳秒的情况)
                    times -= Math.floor(offset / interval) * interval;
                    count += Math.floor(offset / interval);
                    offset = offset % interval;
                }
                var nextTime = interval - offset;    // 标准时间间隔减去此次的误差,为下一次执行的时间
				
                if (Math.floor(times / interval) <= 0) {
                    clearTimeout(timer);
                    showStr = "倒计时:<i>00</i>天<i>00</i>小时<i>00</i>分钟<i>00</i>秒";
                    showDiv.innerHTML = showStr;
                } else {
                    times -= interval;
                    timer = setTimeout(countDownStart, nextTime);
                }
				
                console.log("执行次数:" + count + ", 误差:" + offset + "ms, 下一次执行:" + nextTime + "ms之后, 所剩时间:" + times + "ms");
            }
        }
		
        /** 格式化时间形式 */
        function formatTime(fmt, time){
            fmt = fmt.toUpperCase();
            var day = formatTimeField(Math.floor(time / (1000 * 60 * 60 * 24)));
            var hour = formatTimeField(Math.floor(time / (1000 * 60 * 60)) - (day * 24));
            var minute = formatTimeField(Math.floor(time / (1000 * 60)) - (day * 24 * 60) - (hour * 60));
            var second = formatTimeField(Math.floor(time / 1000) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60));
            var timeField = {
                "D+": day,
                "H+": hour,
                "M+": minute,
                "S+": second
            };
            for(var field in timeField){
                if(new RegExp("(" + field + ")").test(fmt)){
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length===1) ? (timeField[field]) : (("00" + timeField[field]).substr(("" + timeField[field]).length )));
                }
            }
            return fmt;
        }
			
        /** 格式化时间分量 */
        function formatTimeField(number){
            if (number <= 9) {
                number = "0" + number;
            }
            return number;
        }
    </script>
</html>

 

Published 47 original articles · won praise 16 · views 70000 +

Guess you like

Origin blog.csdn.net/zorro_jin/article/details/86018487