【需求】
某应用有一耗时任务,目的是取出一批员工数据显示在页面上,但由于奇特的网络环境,每次调用都要数秒,客户要求在页面上动态显示耗时。
【坑】
动态显示耗时,多会用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