当服务器端执行一个长时间的操作时,页面上如何显示一个反应真实的进度条,报告服务器端执行耗时操作的进度,而不是让用户永远等待,这对用户来说是非常有用的,能够有效提高用户体验(参见《用户体验这点事儿》)。这篇文章使用AJAX,ASP.NET和ExtJS实现这个功能。
实现过程大概如下:
其中第4步是采用客户轮询获得服务器端操作进度的方法,而不是服务器端主动通知(Push模式)客户端,这点注意。使用ExtJS是因为其封装了很好的客户端JS进度条控件,非常方便使用。下面就是具体的分步实现:
改造服务器端耗时操作的WebService,保存当前进度在Context中
1: using System.Threading;
2: using System.Web;
3: using System.Web.Security;
4:
5: public bool ExecuteTimeConsumingTask()
6: {
7: string processKey = HttpContext.Current.Request.UserHostAddress;
8: string threadLockKey = "thread" + HttpContext.Current.Request.UserHostAddress;
9: object threadLock = HttpContext.Current.Cache[threadLockKey];
10:
11: if (threadLock == null)
12: {
13: threadLock = new object();
14: HttpContext.Current.Cache[threadLockKey] = threadLock;
15: }
16:
17: // Only allow 1 running task per user.
18: if (!Monitor.TryEnter(threadLock, 0))
19: return false;
20:
21: int LEN = 1000;
22:
23: int i = 0;
24:
25: for (int i = 0; i < LEN; i++)
26: {
27: //DO Something here...
28:
29: //把进度保存在用户的ContextCache中
30: HttpContext.Current.Cache[processKey] = i * 100 / LEN;
31: }
32:
33: Monitor.Exit(threadLock);
34: return true;
35: }
服务器端提供另一个WebService以供客户端查询上述操作的当前进度
1: public int GetTaskProgress()
2: {
3: string processKey = HttpContext.Current.Request.UserHostAddress;
4: object progress = HttpContext.Current.Cache[processKey];
5:
6: if (progress != null)
7: {
8: return (int)progress;
9: }
10: return 0;
11: }
客户端操作,包括JS轮询服务器端WebService查询进度(异步)并更新进度条等
1: var currentProgress = 0;
2:
3: //显示进度条
4: ShowProgressbar();
5:
6: //触发执行长时间操作的WebService
7: myService.ExecuteTimeConsumingTask(successCallback, failCallback);
8:
9: //显示进度条,轮询服务器长时间操作的进度,并更新进度条
10: function ShowProgressbar(){
11: currentProgress = 0;
12:
13: Ext.MessageBox.show({
14: title: '请稍等...',
15: msg: '服务器端正在处理 ...',
16: progressText: '初始化...',
17: 300,
18: progress:true,
19: closable:false,
20: animEl: 'mb6'
21: });
22:
23: function f(){
24: if(currentProgress < 100)
25: {
26: //触发查询服务器端进度的WebService
27: //查询服务器长时间操作的进度,异步,在callback中检查返回值并更新进度条
28: myService.GetTaskProgress(getProcressSuccessCallback, failCallback);
29:
30: //循环调用,使得轮询效果
31: setTimeout(f, 1000);
32: }
33: };
34: //开始轮询
35: setTimeout(f, 1000);
36: }
37:
38: //回调函数,从服务器端WebService取得长时间操作的进度, 并更新进度条
39: function getProcressSuccessCallback(result){
40: if(currentProgress >= 100) return; //已完成,注意是异步的长时间操作
41:
42: currentProgress = result;
43:
44: if(result >= 100){
45: Ext.MessageBox.hide();
46: }else{
47: var i = result/100;
48: Ext.MessageBox.updateProgress(i, Math.round(100*i)+'% 已完成');
49: }
50: }
51:
52: //回调函数,长时间操作执行完成
53: function successCallback(result){
54: if (result == true)
55: alert('任务成功完成');
56: else
57: alert('任务失败');
58:
59: //设置进度完成,可能异步先返回
60: currentInd = 100;
61:
62: //隐藏进度条
63: if(Ext.MessageBox.isVisible())
64: Ext.MessageBox.hide();
65: }
或许您对以下相关文章有兴趣: