利用JQuery的异步方法和ASP.NET多线程功能,实时获取事务处理进度

整理了好久才完善了这个功能,仅供大家参考。这种功能主要用于一些比较耗时的事务处理。比如大量数据导入导出操作,需要耗费一定时间的复杂计算操作等。为了增加前台体验感受,所以需要实时反馈后台处理进度。

有一种做法是,事务处理期间前台展示一个等待遮罩,但缺点是无法看到事务进度。

本程序缺点是没有展示进度百分比(不过你可以自己添加这个功能),优点是:1、有处理总时间,甚至还可以展示每一个步骤的用时;2、可以实时获取事务进度,进度个数和名称随你定义;3、封装完善,前台页面不需要其他代码,除JS功能代码外。后台代码也进行了一定的封装,方便移植。

先说下原理:

1、开始执行或者提交文件后,利用JQuery的$.post或者$.get或者$.ajax方法不断请求事务进度。

2、如果是第一次发起请求,后台新开线程进行事务计算,如果不是,则只输出进度信息。

以下详细讲解:

共4个文件,.js文件封装功能函数,.css文件修饰遮罩效果,,.aspx页面前台展示,.cs文件后台事务处理。

以下是.js文件,封装了一些功能函数,写法用了JQuery框架(这个框架想必大家都熟悉了)

/* 
遮罩层,一般用于文件上传后显示处理进度
注意:html布局结构不要随意修改,否则样式也需要修改
*/

//显示遮罩层
function ShadeLoad() {

    //遮罩层里的内容
    var t = "";
    t += "<div style=\"float:right;padding:5px 5px 0 0;\"><input type=\"button\" onclick=\"shadeClose()\" value='关闭' /></div>";
    t += "<div class='shadeShowProgress'>";
        t += "<h1>总共用时:<span>-1</span>秒</h1>";
        t += "<h3><div>执行进度提示</div><ol></ol></h3>";
    t += "</div>";

    //创建遮罩
    var sDiv = $("<div class='shadeDiv'></div>").html(t);
    $("body").append(sDiv);

    //遮罩显示(我的页面是运行在iframe框架里的,所以高度需要这样获取,如果不是,请修改获取页面高度方式)
    $(".shadeDiv").css({ "display": "block", "height": "" + $(window.top.document).find("#ifmContent").css("height") + "" });

    //启动计时器
    shadeTimerStart();
}

//关闭遮罩层
function shadeClose() {
    $(".shadeDiv").css({ "display": "none" });                  //隐藏遮罩
    shadeTimerEnd();                                            //停止计时
    $(".shadeShowProgress h1").children("span").html("-1");     //重置计时器(如果不重置,再次提交事务的时候会累积计时)
    $(".shadeShowProgress h3 ol").empty();                      //清空进度内容
}

//展示进度内容
function shadePushMsg(msg) {
    var textArr = $(".shadeShowProgress h3 ol").html();
    if (!msg) { return; }
    if (textArr.indexOf(msg) > -1) { return; }                 //如果获取到的进度是同一个状态,则忽略
    $(".shadeShowProgress h3 ol").append("<li>" + msg + "</li>");
}

var shadeTimerHand;
//启动计时
function shadeTimerStart() {
    $(".shadeShowProgress h1 span").html(function (index, oldValue) {
        return parseInt(oldValue) + 1;
    })
    shadeTimerHand = setTimeout("shadeTimerStart()", 1000);
}
//停止计时
function shadeTimerEnd() {
    clearTimeout(shadeTimerHand);   
}

以上是一个简单的遮罩层,带计时功能,用有序项目方式列出每一次的请求状态。

以下.CSS文件时修饰遮罩层用的,大家可以修改成适合自己的方式:

/* 遮罩层 */
.shadeDiv {
    position: absolute;
    background-color: black;    
    line-height:180%;
    left: 0;
    top: 0;
    width: 100%;
    opacity: 1;        /*不透明度*/
    z-index: 999;
    display: none;
}
    .shadeDiv h1, h2, h3, h4 {
        font-weight: normal;
        font-size: 14px;
    }
    /* 时间和进度显示区 */
    .shadeDiv .shadeShowProgress {
        opacity:1;        /*这里有个疑问待解决,无论怎么修改此值,不透明度不起作用*/
        width: 400px;
        height: 250px;
        overflow-y: scroll;
        margin: 50px auto;
        padding: 20px;
        background-color: white;
        z-index: 9999;
    }
    /* 计秒部分 */
    .shadeDiv h1 span{ color:red; }
    .shadeDiv h3 div{font-weight:bolder;}
    .shadeDiv h3 ol {padding-left:20px; }        /*显示进度文字*/

关于遮罩的事情以上两步就基本解决掉了。

下面是.aspx的前台展示,主要理解如何多次请求事务进度就简单了:(我用的是easyui框架)

<a class="easyui-linkbutton" id="btn" onclick="kaishi('')">点击开始</a>
    <script>
        var hand2;
        function kaishi(zt) {
            var url = "456.ashx";   //我用的是一般处理程序来处理后台代码
            //zt参数主要为了区分是第一次请求还是重复请求
            if (zt == "") {
                //显示遮罩
                ShadeLoad();
            }
            else {
                url += "?action=" + zt;
            }

            //用post方法请求进度
            $.post(url, function (reData) {

                //返回值用json格式,方便处理
                var json = $.parseJSON(reData);                
                shadePushMsg(json.状态);       //列出进度

                if (json.错误) {
                    shadePushMsg("<strong style='color:red'>" + json.错误 + "</strong>");
                    clearTimeout(hand2);       //停止状态请求   
                    shadeTimerEnd();           //停止计时器
                    return;
                }

                if (json.状态 != "完成") {
                    //继续请求状态,开始函数的参数是什么无所谓,只要有就可以,就可以区分是否第一次提交。如果不需要这么频繁得请求,修改毫秒数即可。
                    hand2 = setTimeout("kaishi('1')", 1000);    
                }
                else {
                    clearTimeout(hand2);                        //停止状态请求
                    shadeTimerEnd();                            //停止计时器
                    //easyui函数,如果不清楚的话可以直接用js的alert()
                    $.messager.alert("结果", "操作完成", "info", function () {
                        shadeClose();                        
                    });
                    return;
                }
            });
        }
    </script>

 

以下是.cs功能代码,我用的是.ashx一般处理程序,如果不是的话大家稍微改下就可以。最重要的是程序中的多线程和委托的异步调用写法。这方面的知识请参考ASP.NET程序。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;

public class ajax_456 : IHttpHandler
{
    public static string zhuangtai;         //重要:静态全局变量记录进度信息
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string action = context.Request.QueryString["action"];

        //如果是第一次提交请求
        if (action == null)
        {
            //新开线程进行事务处理
            Thread t = new Thread(new ThreadStart(workThread));
            t.Start();

            //重要:由于是静态变量,第一次使用的时候一定要赋初始值,否则它的值还是上次执行完毕的值(可以参考静态变量的生命周期)。
            //至于多少个步骤由你程序写好后再确定
            zhuangtai = "{\"状态\":\"任务开始(共9个步骤)\"}";     
            context.Response.Write(zhuangtai);
        }
        else
        {
            //如果是重复请求,只反馈回进度状态即可
            context.Response.Write(zhuangtai);
            context.Response.Flush();
            context.Response.End();
        }
    }

    //假设业务复杂,则用类进行封装最好了
    private void workThread()
    {
        操作类 cls = new 操作类();
        cls.主函数();
    }


    public class 操作类
    {
        string wrong = "";                  //记录是否有错误信息
        delegate string mydele();           //委托申明。关于委托请参考其他手册。本例最大化提高事务处理性能,所以使用委托和异步调用的方式
        public void 主函数()
        {
            mydele dele1 = new mydele(获取订单表);
            mydele dele2 = new mydele(获取宝贝表);
            IAsyncResult r1 = dele1.BeginInvoke(null, null);
            IAsyncResult r2 = dele2.BeginInvoke(null, null);

            //关于BeginInvoke和EndInvoke请参考ASP.NET手册
            //简单的可以这么理解:BeginInvoke开启异步调用,程序执行到这一句并不会等它执行完,而是继续往下
            //当遇到EndInvoke时,才会返回异步调用的处理结果。
            //这一知识点请参考:https://blog.csdn.net/wangzhen199009/article/details/40593603
            //或者:https://blog.csdn.net/xxy0403/article/details/51469853
            wrong += dele1.EndInvoke(r1);
            if (!string.IsNullOrEmpty(wrong)) { zhuangtai = "{\"错误\":\"" + wrong + "\"}"; return; }
            wrong += dele2.EndInvoke(r2);
            if (!string.IsNullOrEmpty(wrong)) { zhuangtai = "{\"错误\":\"" + wrong + "\"}"; return; }

            //线程休眠用于演示事务处理所需要的时间
            //至于实际应用中此任务到底用了多少时间,可以用
            //DateTime startDateTime = DateTime.Now;
            //int minSecond=(int)DateTime.Now.Subtract(startDateTime).TotalMilliseconds
            //这两句获得
            Thread.Sleep(3000);
            zhuangtai = "{\"状态\":\"检查宝贝表是否全部在订单表中用时3秒\"}";

            Thread t1 = new Thread(new ThreadStart(更新订单表));
            Thread t2 = new Thread(new ThreadStart(更新宝贝表));
            t1.Start();
            t2.Start();

            t1.Join();
            t2.Join();
            zhuangtai = "{\"状态\":\"完成\"}";
        }

        public string 获取订单表()
        {
            Thread.Sleep(5000);
            zhuangtai = "{\"状态\":\"获取订单表用时5秒\"}";

            Thread.Sleep(2000);
            zhuangtai = "{\"状态\":\"检查订单表用时2秒\"}";

            return "";
        }

        public string 获取宝贝表()
        {
            Thread.Sleep(2000);
            zhuangtai = "{\"状态\":\"获取宝贝表用时2秒\"}";

            Thread.Sleep(2000);
            //如果程序中有需求检测到错误信息则退出线程,可以直接返回错误信息即可。
            //亲们测试的时候可以试试不注解下面这句错误信息。(等下讲解中也会截图这样的效果)
            //return "检查宝贝表发生错误";
            zhuangtai = "{\"状态\":\"检查宝贝表用时2秒\"}";

            return "";
        }

        public void 更新订单表()
        {
            Thread.Sleep(5000);
            zhuangtai = "{\"状态\":\"更新订单表用时5秒\"}";
        }

        public void 更新宝贝表()
        {
            Thread.Sleep(3000);
            zhuangtai = "{\"状态\":\"更新宝贝表用时3秒\"}";
        }
    }

    //.ashx自动生成的方法
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

四个主要文件和步骤就是这样,以下附上测试截图:

以下是程序执行中,每过一秒返回一次状态:

以下是程序顺利执行完成,共返回了8个进度提示。最后的确定框大家可以不需要。笔者这里是再提示状态完成,点击确定后遮罩关闭,如果点击的是提示框的关闭按钮(右上角),则保留遮罩页面。

只是关闭了结果提示框后的界面,再点击遮罩层右上角的关闭按钮,界面回复到初始状态。

以下演示如果程序运行时有错误提示(不是指程序错误,而是事务处理遇到错误信息):

获取到错误信息后程序停止,不再进行下一步。

 

以上就是全部的讲解。刨去示例中的委托和异步调用,这样的代码还算简单的。有不足之处欢迎指正!

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值