`
uule
  • 浏览: 6306733 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

手机版倒计时问题 + 伪异步

 
阅读更多

倒计时:

手机倒计时会有两个问题:

1、返回到倒计时页面时,倒计时不是最新的

2、锁屏后,再打开,倒计时不是最新的

 

经校验发现,后退时,Jquery的该方法都会执行,因此可解决问题1.

$(function(){

 

});

 

 

第一方案

会有问题:后退更新,锁屏不更新

$(function(){	
	$.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()},
		function(data) {
			afterGetNewSeconds(data);
		}
	);
});

 启动方法中加入getTimeCountDown请求,每次获取最新倒计时秒数

 

第二版:

正常,没有问题。

         进入时取一次剩余倒计时秒数m

         请求前纪录客户端时间a,请求后纪录客户端时间b, (b-a)/2 为网络延时

         请求后由当前客户端时间,加上返回的剩余倒计时秒数m,再减去网络延时,得到当前客户端的倒计时结束时间

 

         客户端JS定时器,10秒刷新一次,获取到当前时间与客户端结束时间的秒数差c,刷新倒计时

         若定时器倒计时的秒数(1秒刷一次,10秒后的秒数)与 c 误差在1秒内,不刷新定时器,否则清除老定时器,生成新定时器

 

 具体代码如下:

HTML:

倒计时:<i class="auctTime" id="<@getStringValue cdo=cdoAuction col='nAuctionId' />"></i>

 

timer.js:

var finishArray = [];
var timerArray = [];
//处理网络延时时间
var startTime ;
var endTime ;
//保存歌倒计时定时器,清除定时器时使用
var map = new HashMap();
//保存本地倒计时中的最新秒数,与10秒刷新获取的本地客户端结束时间倒计时比较
var map2 = new HashMap();
var obj = new Object();

$(function(){	

	//获取需倒计时的兑换产品Id
	var timeIds = [];
	$(".auctTime").each(function(){
		var id = $(this).attr("id");		
		timeIds[timeIds.length] = id;
	})
	
	if(timeIds.length == 0) return;
	startTime = new Date();

	//获取最新时间
	$.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()},
		function(data) {
			afterGetNewSeconds(data);
		}
	);


	window.setInterval(refreshCountDown,10000);
});


function timer(intDiff,id){
	var intervalname = "interval"+id;
    var timerVar = window.setInterval(function(){
	    var day=0,
	        hour=0,
	        minute=0,
	        second=0;//时间默认值       
	    if(intDiff > 0){
	        day = Math.floor(intDiff / (60 * 60 * 24));
	        hour = Math.floor(intDiff / (60 * 60)) - (day * 24);
	        minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);
	        second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
	    }
	    hour = day * 24 + hour;
	    if (hour <= 9) hour = '0' + hour;
	    if (minute <= 9) minute = '0' + minute;
	    if (second <= 9) second = '0' + second;
	    
	    var str = "";
	    str += hour + ":";
	    str += minute + ":";
	    str += second;
	    intDiff--;
	    
	    console.log("=======================");
	    map2.put(intervalname,intDiff);
	    $("#" + id).html(str);
	    if(intDiff <= 0){
	    	if(typeof isdetail != "undefined" && isdetail == "1"){
	    		//刷新当前页面
	        	location.reload();
	    	}else{
	    		$("#bidding_"+id).remove();
	    		$(".cd_"+id).hide();
		    	$("#btn_"+id).text("竞拍结束");
		    	clearInterval(timerVar);
	    	}
	    	
	    }
    }, 1000);

    map.put(intervalname,timerVar);
    	
    timerArray[timerArray.length] = timerVar;
}


//获取客户端当前时间与 客户端结束时间作比较,刷新倒计时
function refreshCountDown(){
	if(finishArray.length >= 0){
		for(var i in finishArray){						
			refreshPerCountDown(finishArray[i]);
		}		
	}
	
}

function afterGetNewSeconds(data){	
	endTime = new Date();
	var interval = endTime.getTime() - startTime.getTime();
	data = eval('(' + data + ')');
	if(data.code == 0){
		var result = data.result;
		if(typeof result == "undefined" || result.length == 0) return;
		
		for(var i in result){			
			var exId = result[i].auctionId;
			var newSecond = result[i].seconds;
			var newMiSeconds;
			if(interval > 0){
				//newSecond = newSecond - (interval/1000);
				newMiSeconds = (newSecond * 1000) - (interval/2);
			}
			//计算客户端结束时间
			var finishTime = new Date().getTime() + newMiSeconds;
			var finishTimeStr = exId+"_"+finishTime;
			finishArray[finishArray.length] = finishTimeStr;

			refreshPerCountDown(finishTimeStr);
		}
	}

}


var exId,finishTime,seconds;
//刷新单个 已保存客户端结束时间的倒计时
function refreshPerCountDown(finishTimeStr){
	//clearInterval(intervalVar);
	
	var curTime = new Date().getTime();
	exId = finishTimeStr.split("_")[0];
	finishTime = finishTimeStr.split("_")[1];
	seconds = (finishTime - curTime)/1000;
	
	refreshTimer(exId,seconds);	
}


//刷新最新倒计时
function refreshTimer(exId,newSecond){
	
	var intervalname = "interval"+exId;
	var sed = map2.get(intervalname);
	var diff = parseFloat(sed) - parseFloat(newSecond);
	if(sed == null || diff >=1){
		clearInterval(map.get(intervalname));
		timer(newSecond,exId);
	}
}

 

后台:

/**
	 * 获取最新倒计时
	 */
	public void getTimeCountDown(){
		CDO seconds = new CDO();
		
		String auctionIds = request.getParameter("auctionIds");
		if(StringUtils.isBlank(auctionIds)){
			seconds.setStringValue("code", "-1");
	    	ajaxForAction(response,	seconds.toJSON());
	    	return;
		}
			
		cdoRequest.setStringValue(ITransService.SERVICENAME_KEY, "AuctionService");
    	cdoRequest.setStringValue(ITransService.TRANSNAME_KEY, "getTimeCountDown");
    	cdoRequest.setStringValue("auctionIds", auctionIds);
    	Return ret = getServiceBus().handleTrans(cdoRequest, cdoResponse);
    	if(ret.getCode() == 0){
    		if(cdoResponse.exists("cdosExchange")){
    			CDO[] cdos = cdoResponse.getCDOArrayValue("cdosExchange");
        		CDO[] cdosExchange = new CDO[cdos.length];
        		int i = 0;
        		for(CDO cdo : cdos){
        			String dataString = cdo.getStringValue("ex");
        			//if(StringUtils.isBlank(dataString)) continue;
        			
        			String exId = dataString.substring(0,dataString.indexOf("-"));
        			String strTime = dataString.substring(dataString.indexOf("-")+1);
    				long nSecond = DateUtil.getSecond(strTime); 
    				CDO exchangeCdo = new CDO();
    				exchangeCdo.setStringValue("auctionId", exId);
    				exchangeCdo.setLongValue("seconds", nSecond);
    				cdosExchange[i] = exchangeCdo;
    				i++;
        		}
        		if(cdosExchange.length >0){
        			seconds.setCDOArrayValue("result", cdosExchange);
        		}
    		}
    		
    	}
		
    	seconds.setStringValue("code", "0");
    	
    	ajaxForAction(response,	seconds.toJSON());
	}

 SQL:

select CONCAT_WS("-",a.nAuctionId,a.dtEndTime) ex
				 from tbauction a where a.nAuctionId in (

 。。。

 

 

 最新版优化:

最后1分钟或几分钟内,出价后,时间就变为1分钟,直到无人出价为止

参考了西部数码的竞拍,发现以前考虑的太复杂了,哈哈

西部的JS见附件。

注意的点:

出价成功或失败时,提示使用的不是alert弹窗,而是用的jbox。

alert弹窗出现时,若一直不点,则定时器也会暂停,点击后才继续接着执行,这样如果各客户端点击快慢不同,就会导致各客户端最后的倒计时时间不同。而Jbox则不会阻塞JS,使用jbox.tip也更人性化。

 

参考:

setTimeout和setInterval的深入理解

当有非常多的setTimeout和setInterval时,哪种用法效率高?

解决setInterval计时器不准的问题

 

======================================================================

1、setInterval计时器不准的问题(setInterval回调堆积): 在js中如果打算使用setInterval进行倒数,计时等功能,往往是不准确的,因为setInterval的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行.而下一次触发时间则是在setInterval回调函数执行完毕之后才开始计时,所以如果setInterval内执行的计算过于耗时,或者有其他耗时任务在执行,setInterval的计时会越来越不准,延迟很厉害.

    

2、setTimeout基于JavaScript运行时内部的事件队列 

 jQuery源码里很多地方都用到了setTimeout(callback)可以去看看。打个比方:

 

   function read_data(){

        read_file();

        write_log();

        //setTimeout(write_log,0);

   }

   read_data();

   show_data();

假设write_log()花费1秒,那么read_data()得多等1秒才能返回,然后show_data()才能把数据展示给用户。但是用户根本不关心你的日志,让用户为此多等1秒其实毫无意义,这种场景就可以用setTimeout(callback,0)来调用write_log,让read_data()立刻返回并将数据展示给用户。node里可以用nextTick,会更快一些。

 

伪异步:

【斩草除根】重新认识所谓的“异步” 

我们知道,js是单线程执行的。事实上setTimeout和setInterval只是简简单单地通过插入代码到代码队列来实现代码的延迟执行(或者说异步执行)。但是事实上所谓的异步只是一个假象——它同样运行在一个线程上! 

那么问题就来了,要是在插入点前的代码执行时间超过了传入setTimeout或setInterval的设定时间会怎样呢?让我们来看看这段代码: 

function fn() { 
	setTimeout(function(){alert('can you see me?');},1000); 
	while(true) {} 
} 

 

你觉得这段代码的执行结果是什么呢?答案是,alert永远不会出现。 

这是为什么呢?因为,while这段代码没有执行完,插入在后面的代码便永远不会执行。 

综上所述,其实JS终归是单线程产物。无论如何“异步”都不可能突破单线程这个障碍。所以许多的“异步调用”(包括Ajax)事实上也只是“伪异步”而已。只要理解了这么一个概念,也许理解setTimeout和setInterval也就不难了。 

 

=============================================================================

概要代码:

 

var leftsecond=0;
var timerobj = null ;

$(function(){	

	//获取需倒计时的兑换产品Id
	var timeIds = [];
	$(".auctTime").each(function(){
		var id = $(this).attr("id");		
		timeIds[timeIds.length] = id;
	})
	
	if(timeIds.length == 0) return;
	startTime = new Date();

	//获取最新时间
	$.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()},
		function(data) {
			afterGetNewSeconds(data);
		}
	);

	if(state == "2"){
		daojishi();
	}
	
	//window.setInterval(refreshCountDown,10000);
});

///////////////////////////////////////////////////////////////

function timespan(intDiff){
	console.log("intDiff::::::" + intDiff);
	//if(intDiff <= 0) return;
	var day=0,
    hour=0,
    minute=0,
    second=0;//时间默认值       
	if(intDiff > 0){
	    day = Math.floor(intDiff / (60 * 60 * 24));
	    hour = Math.floor(intDiff / (60 * 60)) - (day * 24);
	    minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);
	    second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);
	}
	hour = day * 24 + hour;
	if (hour <= 9) hour = '0' + hour;
	if (minute <= 9) minute = '0' + minute;
	if (second <= 9) second = '0' + second;
	
	var str = "";
	str += hour + ":";
	str += minute + ":";
	str += second;

	console.log("str:" + str);
	return str;
}

function daojishi(){
	var time = timespan(leftsecond--);
	$("#" + auctionId).text( time );
	clearTimeout(timerobj);
	timerobj = setTimeout("daojishi()",1000)
}


function afterGetNewSeconds(data){	
	endTime = new Date();
	var interval = endTime.getTime() - startTime.getTime();
	data = eval('(' + data + ')');
	//console.log("data=================");
	if(data.code == 0){
		//console.log("data2=================");
		var result = data.result;
		if(typeof result == "undefined" || result.length == 0) return;
		//console.log("data3=================");
		for(var i in result){			
			var exId = result[i].auctionId;
			var newSecond = result[i].seconds;
			
			
			if(parseInt(auctionId) - parseInt(exId) == 0){
				if( Math.abs(newSecond-leftsecond)>=3 ){
					leftsecond = parseInt(newSecond) - 1;	//正负10的情况下才去改变这个数字// 减1是因为网页上timer要延迟一秒
					//console.log("leftsecond=================" + leftsecond);
				}
			}
		}
	}
	
}



function refreshDetailTime(autId,seconds){
	console.log("seconds :" + seconds + ",leftsecond:" + leftsecond);
	if( Math.abs(seconds-leftsecond)>=3 ){
		console.log("refreshDetailTime33====" + leftsecond);
		leftsecond = parseInt(seconds) - 1;	//正负10的情况下才去改变这个数字// 减1是因为网页上timer要延迟一秒
		console.log("refreshDetailTime44  leftsecond====" + leftsecond);
	}
}
 

 

refreshDetailTime 方法为刷出价时,同时刷回来最新的剩余倒计时秒数。

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics