zoukankan      html  css  js  c++  java
  • 如何让Web程序在点击按钮后出现如执行批处理程序般的效果

    在cli程序中,输入命令得到连续的输出已经是一种进度而美观的页面交互形式,好比下图:

    而web程序里也有类似的场景,比如执行一个耗时任务,除了显示出等待图标外,用户还希望把执行的状态及时显示出来.如下图:

    这样的界面如何设计呢?我的思路如下:

    1.点击按钮后,产生一个新ID,后台运行的线程拿到id后开始运行并及时往数据库中插入记录,同时id被送回到前台;

    2.前台拿到id后,开始以此id轮询后台数据表,并将取得的数据显示出来,而取得的数据是后台运行的线程不断写入的;

    3.后台线程写入状态1后,即认为任务完成,前台取得此数据后不再轮询并恢复成初始状态.

    下面请看具体实现:

    前台点击按钮触发Ajax:

        $("#startPhonexCrawl").click(function(){
            var taskId=$("#taskIdTxt").val();
            
            if(taskId!="0"){
                // 有任务启动则取状态
                alert("有任务在执行,请稍等...");
            }else{
                // 没有任务则启动任务
                $.get("/startCrawl/phonex",{},function(data,textStatus){
                    var taskId=data;
                    $("#taskIdTxt").val(taskId);
                    $("#crawlsDiv").html("");
                    $("#loadingImg").show();
                    showTask();
                });
            }
        });

    后台接到请求后一边启动线程,一边将产生的taskid回传:

        /**
         * Start crawl and return crawltask id
         * @param crawlName
         * @return
         */
        @RequestMapping("/startCrawl/{crawlName}")
        String startCrawl(@PathVariable("crawlName") String crawlName) {
            logger.info("准备启动爬虫:"+crawlName);
            long taskId=crawlMapper.getNextTaskId();
            
            BaseCTH cth=null;
            
            if("phonex".equalsIgnoreCase(crawlName)) {
                cth=new PhonexCTH();
                logger.info("Phonex crawl thread is ready.");
            }else if("163".equalsIgnoreCase(crawlName) || "Netease".equalsIgnoreCase(crawlName)) {
                cth=new NeteaseCTH();
                logger.info("Netease crawl thread is ready.");
            }else if("snowball".equalsIgnoreCase(crawlName)) {
                cth=new SnowballCTH();
                logger.info("Snowball crawl thread is ready.");
            }else {
                logger.warn("Error crawlName:"+crawlName+",so no crawl thread started.");
                taskId=0;
            }
            
            if(cth!=null) {
                cth.setTaskId(taskId);
                cth.setStockMapper(stockMapper);
                cth.setCrawlMapper(crawlMapper);
                cth.start();
                logger.info("Crawl thread started.");
            }
            
            return String.valueOf(taskId);
        }

    从上面的程序也可看出,前台按钮和后台具体爬虫联系的纽带是crawlName,这样处理后,如果要增加新爬虫,只要前台做个链接,然后在分支中与具体爬虫联系上即可.

    前台的ajax会在得到返回id后调用showTasks函数:

    function showTask(){
        var taskId=$("#taskIdTxt").val();
        
        if(taskId!="0"){
            $.get("/getCrawlTasks/"+taskId,{},function(data,textStatus){
                var message="";
                var state="";
                var percent="";
                
                for(var i=0,l=data.length;i<l;i++){
                    message+=data[i].ctime+" "+data[i].msg+"<br/>";
                    state=data[i].state;
                    percent=data[i].percent;
                 }
    
                 $("#crawlsDiv").html(message);
                 $("#percentSpan").html(percent+"%");
                 
                 if(state=="1"){
                     //alert("爬虫任务"+taskId+"结束");
                     // 如果任务结束则可启动下一任务
                     clearTimeout(timerHandler);
                     $("#taskIdTxt").val("0");
                     $("#loadingImg").hide();
                     $("#percentSpan").html("");
                 }else{
                     timerHandler=setTimeout("showTask()",3000);
                 }
            });
        }
    }

    showTasks函数会在结束前查看数据状态,如果状态不是1则会以三秒为间隔不断调用自己,从而达到轮询的目的,而轮询取状态的后台函数是

        @RequestMapping("/getCrawlTasks/{taskId}")
        List<CrawlTask> getCrawlTasks(@PathVariable("taskId") String taskId) {
            logger.info("取得taskId="+taskId+"的爬虫状态:");
            
            return crawlMapper.getCrawlTasks(taskId);
        }
        @Select("select id,taskid,state,msg,DATE_FORMAT(ctime,'%Y-%m-%d %T') as ctime,percent from crawltask where taskid=#{taskId} order by id ")
        List<CrawlTask> getCrawlTasks(@Param("taskId") String taskId);

    这样,每过三秒就会从crawltask表里取得信息显示在页面上.

    整套设计里,taskid是前台从db取值和后台线程往数据库写值的联系纽带,有了它的出现,前后台可以在互不知情的情况下良好配合.

    当从后台取得状态为1时,下面语句便会发挥作用:

    clearTimeout(timerHandler);

    timerHandler是启动时的句柄,而一旦clear掉,timeout便不会再起作用,从而结束轮询.

    这就是全部设计过程.

    --2020年5月6日--

  • 相关阅读:
    LeetCode 264. Ugly Number II
    LeetCode 231. Power of Two
    LeetCode 263. Ugly Number
    LeetCode 136. Single Number
    LeetCode 69. Sqrt(x)
    LeetCode 66. Plus One
    LeetCode 70. Climbing Stairs
    LeetCode 628. Maximum Product of Three Numbers
    Leetcode 13. Roman to Integer
    大二暑假周进度报告03
  • 原文地址:https://www.cnblogs.com/heyang78/p/12837015.html
Copyright © 2011-2022 走看看