【JS】利用闭包自制的沙漏类
【需求】
某应用有一耗时任务,目的是取出一批员工数据显示在页面上,但由于奇特的网络环境,每次调用都要数秒,客户要求在页面上动态显示耗时。
【坑】
动态显示耗时,多会用spanId,setInterval(funciton,mseconds),clearInterval(handler),startTime等一套五六个全局变量和函数。如果需要动态显示耗时的地方不止一处,那么区别和维护一堆全局变量和函数必是费力不讨好的事。
【解决方案】
必须用JS类实现,把要显示事件的SpanId,启动时间和句柄三个变量,启动、显示、停止三个动作都归纳到类里去。
【后台耗时和取数的模拟实现】
@RestController public class MockRestCtrl extends Pager{ @RequestMapping(value="/fetchEmps", method=RequestMethod.GET) public Map<String,Object> fetchEmps(int waitSeconds){ try { Thread.sleep(waitSeconds*1000); }catch(Exception ex) { // } Map<String,Object> retvalMap=new LinkedHashMap<String,Object>(); List<Emp> datas=new ArrayList<Emp>(); datas.add(new Emp(1,"Andy",24)); datas.add(new Emp(3,"Bill",34)); datas.add(new Emp(4,"Cindy",44)); datas.add(new Emp(5,"Douglas",54)); datas.add(new Emp(9,"Eliot",64)); retvalMap.put("datas", datas); return retvalMap; } }
上面代码使用了SpringBoot的RestController来实现后台,响应函数接受waitSecond参数,然后用Thread.sleep歇数秒,之后再造数据送回。
有了这样的后台,前台送入不同的waitSeconds就能实现有差别的耗时。
【沙漏类的实现】(本文重点)
function Sandglass(){ var spanId; var handler; var startTime; this.setSpanId=function(id){ spanId=id; } this.start=function(time){ startTime=time;
clearInterval(handler);// 先调用一次,防止用户狂点导致多次调用setInterval
handler=setInterval(this.showElapsed,500); } this.showElapsed=function(){ var now=new Date(); var diff=(now-startTime)/1000; var d=parseInt(diff/86400); var h=parseInt(diff/3600)-24*d; var m=parseInt((diff % 3600) / 60); var s=parseInt(diff % 60); var elapsed=d+"day "+h+"hour "+m+"minute "+s+"second"; document.getElementById(spanId).innerText=" 已耗时:"+elapsed; } this.stop=function(){ clearInterval(handler); } }
以上代码利用闭包实现了私有成员,这些成员只能被函数中定义的函数使用。下面我们可以看看对于使用者来说这个类该怎么用:
【沙漏的初始化】
var sandglass1=new Sandglass(); sandglass1.setSpanId("span1");
【沙漏的启动】
sandglass1.start(new Date());
【沙漏的停止】
sandglass1.stop();
从以上代码可以看出,使用者能用的方法少而简单,较难出错。Sandglass相对复杂的细节把控在了作者手中。
【实现效果】

【前台HTML代码】
<table> <tr> <td width=50%> <table border="1" class="table"> <caption>Table1<span id="span1"></span></caption> <thead> <tr> <td width="120px">id</td> <td width="120px">Name</td> <td width="120px">Age</td> </tr> </thead> <tbody id="table1"> </tbody> </table> </td> <td width=50%> <table border="1" class="table"> <caption>Table2<span id="span2"></span></caption> <thead> <tr> <td width="120px">id</td> <td width="120px">Name</td> <td width="120px">Age</td> </tr> </thead> <tbody id="table2"> </tbody> </table> </td> </tr> </table>
【前台JS代码】
<script type="text/javascript">
var sandglass1=new Sandglass();
sandglass1.setSpanId("span1");
var sandglass2=new Sandglass();
sandglass2.setSpanId("span2");
fillTable('table1',3,sandglass1);
fillTable('table2',5,sandglass2);
function fillTable(tableId,waitSeconds,sandglass){
sandglass.start(new Date());
$.ajax({
url:"/mediacool/fetchEmps",
data:{waitSeconds:waitSeconds},
type:"get",
dataType:"json",
timeout:50000,
error:function(xhr,textStatus,errorThrown){alert('ajax error')},
success:function(rsps){
sandglass.stop();
showDatasInTable(rsps.datas,tableId);
},
});
}
function showDatasInTable(datas,tableId){
var table=document.getElementById(tableId);
// remove remained rows
var trs=table.childNodes;
for(var i=trs.length-1;i>=0;i--){
table.removeChild(trs[i]);
}
// add new rows
for(var i=0,n=datas.length;i<n;i++){
var data=datas[i];
var td1=document.createElement("td");
td1.appendChild(document.createTextNode(data.id));
var td2=document.createElement("td");
td2.appendChild(document.createTextNode(data.name));
var td3=document.createElement("td");
td3.appendChild(document.createTextNode(data.age));
var tr=document.createElement("tr");
tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
if(i % 2==0){
tr.style.backgroundColor="#f5f2eb";
}
table.appendChild(tr);
}
}
function Sandglass(){
var spanId;
var handler;
var startTime;
this.setSpanId=function(id){
spanId=id;
}
this.start=function(time){
startTime=time;
handler=setInterval(this.showElapsed,500);
}
this.showElapsed=function(){
var now=new Date();
var diff=(now-startTime)/1000;
var d=parseInt(diff/86400);
var h=parseInt(diff/3600)-24*d;
var m=parseInt((diff % 3600) / 60);
var s=parseInt(diff % 60);
var elapsed=d+"day "+h+"hour "+m+"minute "+s+"second";
document.getElementById(spanId).innerText=" 已耗时:"+elapsed;
}
this.stop=function(){
clearInterval(handler);
}
}
</script>
END
浙公网安备 33010602011771号