Ένα πεντάλεπτο παράδειγμα ασύγχρονης σελίδας. Μια σελίδα, ξεκινάει ασύγχρονα μια διαδικασία, η οποία τρέχει στο background και κάνει .. κάτι. Η σελίδα κάνει κάθε 15 δεύτερα refresh, και δείχνει αν έχει τελειώσει η διαδικασία στο background. Όταν με το καλό τελειώσει, δείχνει το αποτέλεσμα της διαδικασίας.
Τα βασικά πρώτα. Η κλάσση η οποία υλοποιεί την όποια διαδικασία:
public class AsyncOperation
{
public const int DURATION = (1000 * 25);
public AsyncOperation()
{
}
public string DoSomething(object data) {
Thread.Sleep(DURATION);
return "finished";
}
public delegate string AsyncHandler(object data);
}
Το όλο μυστικό είναι ο delegate, με το ίδιο footprint με τη μέθοδο που θέλουμε να εκτελεστεί ασύγχρονα. Το framework θα κάνει τα απαραίτητα, έτσι ώστε να μπορέσουμε μέσω του delegate να χρησιμοποιήσουμε τη μέθοδο DoSomething ασύγχρονα ...
Και μετά η σελίδα που την χρησιμοποιεί ασύγχρονα:
public class AsyncForm : System.Web.UI.Page
{
public const string SESS_PARAM_OPERATION = "__asyncOperation";
public const string SESS_PARAM_RESULT = "__asyncResult";
public const string SESS_PARAM_DELEGATE = "__asyncDelegate";
protected AsyncOperation Operation {
get {
return (AsyncOperation)Session[SESS_PARAM_OPERATION];
}
set {
Session[SESS_PARAM_OPERATION] = value;
}
}
protected AsyncOperation.AsyncHandler DelegateHandler {
get {
return (AsyncOperation.AsyncHandler)Session[SESS_PARAM_DELEGATE];
}
set {
Session[SESS_PARAM_DELEGATE] = value;
}
}
protected IAsyncResult OperationResult {
get {
if(null==Session[SESS_PARAM_RESULT] && null!=this.Operation){
this.DelegateHandler = new AsyncOperation.AsyncHandler(this.Operation.DoSomething);
Session[SESS_PARAM_RESULT] = this.DelegateHandler.BeginInvoke("test data", null, null);
}
return (IAsyncResult)Session[SESS_PARAM_RESULT];
}
}
protected bool KeepPolling {
get {
if(null==Session["poll"])
Session["poll"] = false;
return (bool)Session["poll"];
}
set {
Session["poll"] = value;
}
}
private void Page_Load(object sender, System.EventArgs e)
{
// Check we've started the operation yet ..
if(null==Operation){
Operation = new AsyncOperation();
this.KeepPolling = true;
Response.Write("Started AsyncOperation");
}
else
if(this.KeepPolling)
{
bool isComplete = this.OperationResult.IsCompleted;
if(!isComplete)
Response.Write("Processing"); // Poll the result ...
else {
string result = this.DelegateHandler.EndInvoke(this.OperationResult);
this.KeepPolling = false;
Response.Write(string.Format("Result: {0}", result));
}
}
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
Λοιπόν, εδώ τώρα είναι το ζουμί. Βασικά η διαδικασία δεν είναι και πολύ πολύπλοκη. Δημιουργούμε ένα νέο instance του delegate, του οποίου η BeginInvoke μας επιστρέφει ένα IAsyncResult object. Αυτό ενημερώνεται όταν τελειώσει η ασύγχρονη εκτέλεση, οπότε πλέον καλούμε την EndInvoke για να πάρουμε το αποτέλεσμα. Το "όποιο" μυστικό είναι να κρατάς αυτά τα instances στο Session, στην Cache, κάπου τέλος πάντων, έτσι ώστε να είσαι οκ. Δεν μπορείς να τα δημιουργείς κάθε φορά.
Επίσης, είναι σημαντικό να κρατάς και κάπου ένα flag για το αν καλέστηκε η EndInvoke, γιατί αν καλεστεί δεύτερη φορά θα πάρεις exception πίσω.
Α! Στο HTML της σελίδας, έχω ένα META REFRESH tag για να κάνει refresh κάθε 15 δεύτερα. Θα δείτε στον κώδικα ότι η ασύγρχονη δαιδικασία τελειώσει σε 25, έτσι ώστε να δεί κανείς όταν το τρέξει και τις τρείς φάσεις (started, processing, results).
Ελπίζω να ήταν ενδιαφέρον, αν όχι χρήσιμο, το παραδειγματάκι ;]
Happy async coding ! :)
Υ.Γ. .. συνημμένο το project !
Angel
O:]