Running slow server procedures - marcos8154/SocketAppServer GitHub Wiki
Some applications are by design, long and time consuming procedures where it is not possible to return an immediate response. To assist in this scenario, the framework provides sophisticated thread control so you can implement your slow code without affecting client response.
Rather, we need to describe a hypothetical scenario where this can be applied.
A purchase order processor
Let's illustrate with a hypothetical purchase processing task, where it takes several steps and checks before ordering, such as access to external services, various data calculations, and the like.
In this situation, the client sends a request to process the request, in which case the server will respond with a status similar to the "Accepted" HTTP world.
By the time the server receives the request to close the request, it will start a new process or thread to perform the lengthy procedure, and at the same time respond to the client that just "accepted" the request.
The server will also make certain Actions available to query the status or progress of the order, which the client will periodically query.
Given the illustration, let's go to the code!
The AsyncTask Class
The AsyncTask class, located in the "MobileAppServer.ServerObjects" namespace, is responsible for executing Threads in a sophisticated, tightly typed, step-by-step manner.
This class is strongly typed, having 3 types in its definition:
//AsyncTask definition
public abstract class AsyncTask<TParams, TProgress, TReturn>
- TParams: This type will define the parameter object that will be injected into the Thread.
- TProgress: This type will set the sent object as progress, when the Thread makes execution progress available.
- TReturn: This type will define the thread's return object as soon as it is terminated.
Anatomy of an AsyncTask
public class MySimpleTask : AsyncTask<string, string, string>
{
public override string DoInBackGround(string param)
{
//execute slow procedures here
return "return of task";
}
public override void OnPostExecute(string result)
{
//code executed AFTER thread end
}
public override void OnPreExecute()
{
//code executed BEFORE thread start
}
public override void OnProgressUpdate(string progress)
{
//Code executed when Thread posts progress
}
}
Notice the signature of our class:
public class MySimpleTask : AsyncTask<string, string, string>
Note that we are passing 3 types to AsyncTask as strings We could pass any type, but basically we're saying this:
"This is a thread that takes a string as a parameter, posts progress notifications in string format, and when it finishes processing returns a string"
Keep this in mind ALWAYS
How to post progress
Within the DoInBackground (and ALWAYS IN) method, call the abstract class method "UpdateProgress()" It will request as parameter the same type that was defined as "TProgress" in our class signature
public override string DoInBackGround(string param)
{
//execute slow procedures here
UpdateProgress("Progress of my Thread!");
return "return of task";
}
Never directly call the "OnProgressUpdate()" method. This is wrong and may cause problems with your Thread execution.
public override void OnProgressUpdate(string progress)
{
/*
* As in the example above,
* here will be sent the
* string "Progress of my Thread!"
**/
}
Canceling Thread Execution
Inside the DoInBackground (and ALWAYS IN) method, invoke the CancelTask() method. It asks for a bool parameter, which determines whether the OnPostExecute() method should be invoked, or ignored.
public override string DoInBackGround(string param)
{
//execute slow procedures here
UpdateProgress("Progress of my Thread!");
if (anyCondition)
CancelTask(false); //cancel task and not run OnPostExecute()
return "return of task";
}
Note that by canceling a task, the rest of the process will be taken along and the thread will be destroyed.
Why can't I invoke the OnPreExecute(), OnPostExecute(), OnProgressUpdate() methods directly from within DoInBackground()?
These calls cannot happen this way because inside an AsyncTask, the only method that is actually inserted into the thread is DoInBackground.
The other methods are designed to stay out of the thread, are invoked by the AsyncTask controller.
These calls are synchronized automatically by AsynTask. When you invoke such methods from within DoInBackground, you are nothing but executing the methods INSIDE the Thread, which is wrong, as they are meant to communicate with the outside world.
How to perform our Task
Instantiate your Task class normally. After that, invoke the Execute (TParams) method.
It will request the parameter object as defined in its class signature
public ActionResult ProcessOrder(PurchaseOrder order)
{
PurchaseProcessTask task = new PurchaseProcessTask();
task.Execute(order);
return ActionResult.Json(new OperationResult(true, 600, "Your purchase is accepted"));
}
After that, return a response to the client saying only that you received the request. Nothing more, nothing less.
Also provide an Action so that clients can check the progress of the operation:
public ActionResult GetOrderStatus(Guid orderId)
{
//Here you will consult the progress of the order
return ActionResult.Json(....);
}
How will I know about the progress of my Thread?
As you may have understood before, Thread is an isolated box. You cannot see what happens inside. That's why the other AsyncTask methods exist.
Inside, you can write information to a data repository, and Query Actions will check those repositories and return progress to Clients.
This information can be persisted when the OnPreExecute(), OnPostExecute(), and OnProgressUpdate() methods are called.
The sky is the limit. Have a good time!
Bonus: passing more than one parameter to AsyncTask
As we have seen, the AsyncTask class is typed, and one of the types is TParam, which defines the parameter object that will be transferred to DoInBackground.
The problem is that on several occasions we need to pass not only one, but a series or set of several parameters. We can create a DTO class in our project to do this. But AsyncTask already has its own way of handling it.
The TaskParams Class
Modify the definition of your Task in the type where TParam is entered. Set as TaskParams
To retrieve parameter values, use the "Get" methods of the TaskParams class, as shown below:
public class PurchaseProcessTask : AsyncTask<TaskParams, string, string>
{
public override string DoInBackGround(TaskParams param)
{
decimal price = param.GetDecimal("price");
bool isActive = param.GetBool("isActive");
string anyOtherParam = param.GetString("other");
PurchaseOrder order = (PurchaseOrder)param.GetValue("purchaseObj");
UpdateProgress("Progress of my Thread!");
return "return of task";
}
.....
To transfer the parameters to AsyncTask, do as the example below in call
PurchaseProcessTask task = new PurchaseProcessTask();
task.Execute(TaskParams.Create()
.Set("price", 14.50m)
.Set("isActive", true)
.Set("other", "Any other param string")
.Set("order", order));