先简单介绍下场景
按条件进行大量数据的查询,因为数据量大的原因,导致可能40秒结果都还没有出来,所以后台采用多线程实现异步的处理方式,先返回;但是问题是查询完成的时候,怎么可以让查询的结果自动展示到前台页面呢?
采用方案:
给被查询页面增加刷新标志,表里面增加字段status,值只能是0或1,0:不需要刷新,1:需要刷新
status的默认值是0。
页面点击查询按钮:
1进度条展示开始,数据统计并入表开始,在统计完成的时候,将status更新为1,怎么确定数据统计完成呢,所有多线程的future.get调用完毕
2页面增加定时查看status的任务开始,status=0,不需要刷新;status=1,需要刷新页面,将status重置为0,返回需要刷新页面的标志,前端进行页面的刷新,同时删除页面里面的js定时器和进度条。
整体代码
点击查询按钮调用的方法--开始查询,并且开始定时任务
// 全局变量 定时器
var t1;
/**
* ajax异步调用多线程开始数据统计,并且开始定时器任务
*
* @param pageId
* @param pageType
*/
function queryAndSaveResult(pageId,pageType){
// 参数准备及整理
// 这里页面增加加载进度条
__showLoading();
// ajax请求开始数据查询
$.ajax({
type: "POST",
url: "../bireport/queryAndSaveResult.action",
contentType : "application/json",
timeout:20000,
dataType:"html",
data: JSON.stringify(json),
success: function(resp){
// 开启定时器获取status,status=0:不需要刷新;status=1,需要刷新页面,且刷新后将status重置为0,同时删除页面里面的js定时器
t1=window.setInterval("refreshPage(pageId,pageType,t1)", 1000);
},
error:function(resp){
$.messager.alert('出错了','系统出错,请联系管理员。','error');
}
});
}
后台的查询代码:统计数据,统计数据完成后,异步更新status为1
/**
* 功能描述: 按照查询条件保存日活数据
*
* @param:
* @return:
* @auther: mazhen
* @date: 2018/9/3 下午8:03
*/
@RequestMapping(value="/queryAndSaveResult.action", method = RequestMethod.POST)
public @ResponseBody
Object queryAndSaveResult(@RequestBody ResultCondition timeInterval) throws InterruptedException/* throws Exception*/ {
//1、查询参数整理与数据准备
//2采用了线程池,异步进行数据查询和保存
ArrayList<Future> futures = new ArrayList<>();
for(final RhQueryConditionNew dto:rhQueryConditionDtos){
times = timeOfCondition.get(dto.getId());
// 逐日往后推,判断改日的数据是否已经存在,如果已经存在则不查询
while(currentTimems<=endTimems) {
// 说明该条件下的该日期没有查询过,则if里面查询从改日到最后一日的数据
if (!times.contains(dateFormat1.format(currentTimems))) {
isSleep = true;
Future<?> submit = fixedThreadPool.submit(new QueryTask(dto, dateFormat1.format(currentTimems), endTimeStr, pageId, pageType, rhService));
futures.add(submit);
// 上面查询到最后日期的数据所以没有必要再进行循环判断了,直接break
break;
}
currentTimems += oneDay;
}
}
// 新开一个异步线程,设置线程,futures执行完毕设置status为1,表示需要刷新展示
new Thread(new PageStatusTask(futures,pageId,olapService)).start();
Result ret = new Result();
ret.setResult(RequestStatus.SUCCESS.getStatus());
return ret;
}
后台异步设置status的线程代码:数据统计完成,将status更新为1
public class PageStatusTask implements Runnable{
static Logger logger = Logger.getLogger ( PageStatusTask.class ) ;
ArrayList<Future> futures ;
String pageId ;
OlapService service;
public PageStatusTask(ArrayList<Future> futures ,String pageId,OlapService service){
this.futures =futures;
this.pageId = pageId;
this.service = service;
}
@Override
public void run() {
for(Future future : futures){
try {
future.get();
} catch (Exception e) {
logger.error("");
}
}
// 所有线程的future.get执行完毕,说明查询已经完成,可以刷新页面的展示了
service.updateOlapStatus(Integer.parseInt(pageId), 1);
}
}
前端获取status的值并进行对应的操作
/**
* 定时查看当前页面是否需要刷新展示,status=0,不需要刷新;status=1,需要刷新页面,且刷新后将status重置为0,同时删除页面里面的js定时器
* @param pageId 页面id
* @param pageType 页面类型
* @param t1
*/
function refreshPage(pageId,pageType,t1){
var startTime = $("#dfm1").val();
var endTime= $("#dfm2").val();
var json = {
pageId:pageId
};
$.ajax({
type: "POST",
url: "../bireport/queryAndResetPageStatus.action",
contentType : "application/json",
timeout:2000000,
dataType:"html",
data: JSON.stringify(json),
success: function(resp){
// status=0,不需要刷新;status=1,需要刷新页面,且刷新后将status重置为0,同时删除页面里面的js定时器
if("1"==resp){
openRhShowPage(startTime,endTime,pageId,pageType);
__hideLoading();
window.clearInterval(t1);
}else{
console.log("still querying---");
}
},
error:function(resp){
$.messager.alert('出错了','系统出错,请联系管理员。','error');
}
});
}
前端定时任代码逻辑如下:先获取status,status=0,不需要刷新;status=1,需要刷新页面,且刷新后将status重置为0,同时删除页面里面的js定时器
使用js定时器的注意点
方法带引号不不带引号的区别:带引号会一直执行,但是如果不带引号则仅仅执行一次
如;
window.setInterval("refreshPage(pageId,pageType,t1)", 1000);//一直执行
window.setInterval(refreshPage(pageId,pageType,t1), 1000);//执行一次
定时的方法中的参数,一定需要是全局变量,如果参数时局部变量,就会出现第一次方法能正常执行,但是第二次及以后参数就变为undefined了。如上面refreshPage(pageId,pageType,t1)中的pageId和pageType。
进度条显示和隐藏的代码如下
function __showLoading(){
var sload = $('#loadingdiv');
if(sload.size() == 0){
sload = $('<div id="loadingdiv" class="sk-spinner sk-spinner-three-bounce" style="position:absolute;z-index:9999"><div class="sk-bounce1"></div><div class="sk-bounce2"></div><div class="sk-bounce3"></div></div>').appendTo('body');
window.loadCompCnt = 1;
}else{
window.loadCompCnt = window.loadCompCnt + 1;
}
var doc = $(document);
var win = $(window);
var t = doc.scrollTop() + win.height()/2 - 50;
var l = doc.scrollLeft() + win.width()/2 - 50;
sload.css({'top':t, 'left':l});
sload.show();
}
function __hideLoading(){
window.loadCompCnt = window.loadCompCnt - 1;
if(window.loadCompCnt == 0){
$("#loadingdiv").remove();
delete window.loadCompCnt;
}
}