In a web application, some long running tasks may time out and not finish. To avoid this kind of limitation we will show you how to run such tasks reliably and, at the same time provide progress status to the end user so they know the task is still running. In this article we’ll show you how to achieve this in PHPRunner and ASPRunner.NET.
A common task like this is batch sending email notifications. The key is to initiate a process like this from JavaScript sending the next batch of tasks to the server via AJAX.
1. Button on the List page.
First, we will add a new button to the Customers list page titled ‘Send emails’. The following code goes to ClientBefore event. Leave Server and ClientAfter events empty.
// how many emails to send per stepvar recCount = pageObj.proxy["recCount"], mail\_per\_step = 10;// basic HTML code for the progress popup windowvar html = "<div class><b>Sent mails: <span class='sent'>0</span> of <span>" + recCount + "</span></b></div>" + "<br><div>Mailed: <span class='mailed'>0</span>, Errors: <span class='errors'>0</span></div>";// showing the progress in a popupvar popup = Runner.displayPopup({html: html,header: 'Sending...',afterCreate: function (win) {// starting sending emails, ajaxStep is a secursive functionajaxStep(0, mail\_per\_step);}});function ajaxStep(step, mail\_per\_step) {$.get("", { ajaxMail: true, ajaxstep: step, mail\_per\_step: mail\_per\_step }, function (response) {var json = JSON.parse(response);// using the response data to update the progress popup$(".sent").html(parseInt($(".sent").html()) + json["totalSent"]);$(".mailed").html(parseInt($(".mailed").html()) + json["mailed"]);$(".errors").html(parseInt($(".errors").html()) + json["errors"])if (parseInt(json["totalSent"]) < mail\_per\_step) {// time to exit the recursion. The number of processed records is less than// number of records per step which means we ran out of data to processswal("Done");} else {// otherwise proceed to the next stepstep++;ajaxStep(step, mail\_per\_step);};});}return false;
2. List page, BeforeDisplay event.
In this event we simply calculate the number of records in the customers table and pass it to Javascript. This way our Javascript code knows how many emails to send.
Modify the SQL query to match your database structure.
PHP:
$sql = "select count(*) from customers";$recCount = DB::DBLookup($sql);$pageObject->setProxyValue("recCount",$recCount);
C#
dynamic recCount = null, sql = null;sql = new XVar("select count(*) from customers");recCount = XVar.Clone(DB.DBLookup((XVar)(sql)));pageObject.setProxyValue(new XVar("recCount"), (XVar)(recCount));
3. After Table Initialized event.
This is where we actually run our job.
PHP
if( postvalue("ajaxMail") != false ){$response = array("totalSent" => 0, "errors" => 0,"mailed" => 0);// number of records to process per step$recCount = postvalue("mail\_per\_step");// starting record$startRecord = intval(postvalue("ajaxstep"))*$recCount;// name of the email field$email\_field = "email";// sql query$sql = "select * from customers limit ".$startRecord.",".$recCount;$rs = DB::Query($sql);// the main cycle where we perform our taskswhile($data = $rs->fetchAssoc()){$mail\_params = array('to' => $data[$email\_field], 'subject' => "Ajax notification", 'htmlbody' => "body text");$mailed = runner\_mail($mail\_params);if($mailed["mailed"])$response["mailed"]++;else$response["errors"]++;$response["totalSent"]++;}// return the responseecho my\_json\_encode($response);exit();}
C#
if (MVCFunctions.postvalue(new XVar("ajaxMail")) != false) {dynamic data, rs, email\_field = null, mail\_params = null, mailed = XVar.Array(), recCount = null, sql = null, startRecord = null, var\_response = XVar.Array();var\_response = XVar.Clone(new XVar("totalSent", 0, "errors", 0, "mailed", 0));recCount = XVar.Clone(MVCFunctions.postvalue(new XVar("mail\_per\_step")));startRecord = XVar.Clone((int) MVCFunctions.postvalue(new XVar("ajaxstep")) * recCount);email\_field = new XVar("email");sql = XVar.Clone(MVCFunctions.Concat("select * from customers limit ", startRecord, ",", recCount));rs = XVar.Clone(DB.Query((XVar)(sql)));while (XVar.Pack(data = XVar.Clone(rs.fetchAssoc()))) {mail\_params = XVar.Clone(new XVar("to", data[email\_field], "subject", "Ajax notification", "htmlbody", "body text"));mailed = XVar.Clone(MVCFunctions.runner\_mail((XVar)(mail\_params)));if (XVar.Pack(mailed["mailed"])) {var\_response["mailed"]++;} else {var\_response["errors"]++;}var\_response["totalSent"]++;}MVCFunctions.Echo(MVCFunctions.my\_json\_encode((XVar)(var\_response)));MVCFunctions.ob\_flush();HttpContext.Current.Response.End();throw new RunnerInlineOutputException();}
Happy coding!