另一个简单的等待页面
本文演示如何使用异步委托调用和客户端脚本回调来构建长时间等待页面。
引言
有时您需要在 Web 表单中处理一项耗时的任务。构建一个等待页面,而不是让用户只是盯着屏幕无所事事,会更好。
背景
互联网上有很多解决方案可以完成此目的。我的解决方案并不新颖,最初的想法来自 Brian Dunnington 的文章 构建一个更好的等待页面,发表在 CodeProject 上。我已经在 ASP.NET 1.1 中使用了这种方法很长时间。自从 ASP.NET 2.0 发布以来,Microsoft 使客户端回调的实现变得更加容易,我决定扩展此解决方案以包含 AJAX 功能,这样我们就不需要不时地刷新页面了。
使用代码
在这个解决方案中,您需要考虑 3 个主要方面
- 异步委托调用
- 与客户端回调关联
- 客户端脚本
异步委托调用
/// <summary>
/// Definition of delegate
/// </summary>
/// <param name="minute"></param>
/// <returns></returns>
private delegate bool DoJobDelegate (int minute);
/// <summary>
/// To invoke the long process function
/// </summary>
/// <param name="minute"></param>
/// <returns></returns>
private IAsyncResult DoJobAsync(int minute) {
DoJobDelegate doDelegate = new DoJobDelegate(doLongJob);
IAsyncResult ar = doDelegate.BeginInvoke(minute,
new AsyncCallback(MyCallback), null);
return ar;
}
/// <summary>
/// The server side callback handler
/// </summary>
/// <param name="ar"></param>
private void MyCallback (IAsyncResult ar) {
AsyncResult aResult = (AsyncResult)ar;
DoJobDelegate doDelegate = (DoJobDelegate)aResult.AsyncDelegate;
// Session object is used to tell if process finishes or not
Session["NewOrderResult"] = doDelegate.EndInvoke(ar);
}
/// <summary>
/// The main function to run long process
/// </summary>
/// <param name="minute"></param>
/// <returns></returns>
private bool doLongJob (int minute) {
System.Threading.Thread.Sleep(minute * 1000 * 60);
if (minute % 2 == 0) {
return true;
} else {
return false;
}
}
与客户端回调关联
为了与客户端回调关联,您必须实现 ICallbackEventHandler 接口
public partial class Process : System.Web.UI.Page, ICallbackEventHandler {
protected string CallBackEventReference;
...
}
然后我们需要准备脚本来引用客户端函数
string ScriptRef = this.ClientScript.GetCallbackEventReference(
this,
"'CheckStatus'",
"ClientCallBack",
"this",
"ClientCallBack",
true);
CallBackEventReference = ScriptRef;
这将创建一个类似于 WebForm_DoCallback('__Page','CheckStatus',ClientCallBack,this,ClientCallBack,true); 的客户端函数
从这一点开始,您需要实现该接口的 2 个函数
/// <summary>
/// Capture the event argument in this field, in this case I don't use it.
/// </summary>
string eventArgument = "";
/// <summary>
/// Returns result to client side
/// </summary>
/// <returns></returns>
string ICallbackEventHandler.GetCallbackResult() {
if (Session["NewOrderResult"] != null) {
// after async call finishes,
// it sets this Session object to some value,
// this method will capture the status
if (Convert.ToBoolean(Session["NewOrderResult"])) {
return "even.htm";
} else {
return "odd.htm";
}
} else {
return "";
}
}
/// <summary>
/// Gets parameter passed from client side
/// </summary>
/// <param name="eventArgument"></param>
void ICallbackEventHandler.RaiseCallbackEvent (string eventArgument) {
this.eventArgument = eventArgument;
}
客户端脚本
在客户端,我们需要准备一些与服务器通信的 JavaScript 函数。
function ClientCallBack(Result, Context) {
if (Result != "") {
window.location.href = Result;
}
}
为了让浏览器检查服务器处理状态,我们还需要一个计时器来让客户端脚本以一定的间隔运行。
<body onload="startClock();">
// check server every 5 seconds, adjust this value to your own preference
var interval = 5
var x = interval
function startClock(){
x = x - 1;
setTimeout("startClock()", 1000);
if(x == 0){
<%= this.CallBackEventReference %>
现在发生了什么?客户端脚本将每 5 秒触发一次与服务器通信。服务器端 ICallbackEventHandler.GetCallbackResult() 方法将每 5 秒被调用一次。在这个方法中,它检查 Session 对象的值,如果异步调用完成了处理,它将调用 MyCallback 将 Session 对象设置为一个非空值,该值来自处理结果,以便 ICallbackEventHandler.GetCallbackResult() 能够捕获该结果。
关注点
我对客户端回调的第一次探索,我希望以后添加更多功能。
历史
- 2006-10-11 初始版本。