{"id":2989,"date":"2023-08-28T18:57:03","date_gmt":"2023-08-28T23:57:03","guid":{"rendered":"https:\/\/xlinesoft.com\/blog\/?p=2989"},"modified":"2023-11-05T18:41:43","modified_gmt":"2023-11-05T23:41:43","slug":"long-running-process-with-notifications","status":"publish","type":"post","link":"https:\/\/xlinesoft.com\/blog\/2023\/08\/28\/long-running-process-with-notifications\/","title":{"rendered":"Long running process with notifications"},"content":{"rendered":"<p>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&#8217;ll show you how to achieve this in PHPRunner and ASPRunner.NET.<\/p>\n<p>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.<\/p>\n<p><a href=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-2993\" src=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process-600x403.png\" alt=\"\" width=\"600\" height=\"403\" srcset=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process-600x403.png 600w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process-1024x688.png 1024w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process-768x516.png 768w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2023\/08\/long_process.png 1163w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><br \/>\n<!--more--><\/p>\n<p><strong>1. Button on the List page.<\/strong><\/p>\n<p>First, we will add a new button to the Customers list page titled &#8216;Send emails&#8217;. The following code goes to ClientBefore event. Leave Server and ClientAfter events empty.<\/p>\n<p>&nbsp;<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\n\r\n\/\/ how many emails to send per step\r\nvar recCount = pageObj.proxy[\"recCount\"], mail_per_step = 10;\r\n\r\n\/\/ basic HTML code for the progress popup window\r\nvar html = \"&lt;div class&gt;&lt;b&gt;Sent mails: &lt;span class='sent'&gt;0&lt;\/span&gt; of &lt;span&gt;\" + recCount + \"&lt;\/span&gt;&lt;\/b&gt;&lt;\/div&gt;\" + \"&lt;br&gt;&lt;div&gt;Mailed: &lt;span class='mailed'&gt;0&lt;\/span&gt;, Errors: &lt;span class='errors'&gt;0&lt;\/span&gt;&lt;\/div&gt;\";\r\n\r\n\/\/ showing the progress in a popup\r\nvar popup = Runner.displayPopup({\r\nhtml: html,\r\nheader: 'Sending...',\r\nafterCreate: function (win) {\r\n\/\/ starting sending emails, ajaxStep is a secursive function\r\najaxStep(0, mail_per_step);\r\n}\r\n});\r\n\r\nfunction ajaxStep(step, mail_per_step) {\r\n$.get(\"\", { ajaxMail: true, ajaxstep: step, mail_per_step: mail_per_step }, function (response) {\r\nvar json = JSON.parse(response);\r\n\r\n\/\/ using the response data to update the progress popup\r\n\r\n$(\".sent\").html(parseInt($(\".sent\").html()) + json[\"totalSent\"]);\r\n$(\".mailed\").html(parseInt($(\".mailed\").html()) + json[\"mailed\"]);\r\n$(\".errors\").html(parseInt($(\".errors\").html()) + json[\"errors\"])\r\n\r\nif (parseInt(json[\"totalSent\"]) &lt; mail_per_step) {\r\n\/\/ time to exit the recursion. The number of processed records is less than\r\n\/\/ number of records per step which means we ran out of data to process\r\nswal(\"Done\");\r\n} else {\r\n\/\/ otherwise proceed to the next step\r\nstep++;\r\najaxStep(step, mail_per_step);\r\n};\r\n});\r\n}\r\nreturn false;<\/textarea><\/pre>\n<\/div>\n<p><strong>2. List page, BeforeDisplay event.<\/strong><\/p>\n<p>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.<\/p>\n<p>Modify the SQL query to match your database structure.<\/p>\n<p><strong>PHP:<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$sql = \"select count(*) from customers\";\r\n$recCount = DB::DBLookup($sql);\r\n$pageObject-&gt;setProxyValue(\"recCount\",$recCount);<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic recCount = null, sql = null;\r\nsql = new XVar(\"select count(*) from customers\");\r\nrecCount = XVar.Clone(DB.DBLookup((XVar)(sql)));\r\npageObject.setProxyValue(new XVar(\"recCount\"), (XVar)(recCount));<\/textarea><\/pre>\n<\/div>\n<p><strong>3. After Table Initialized event.<\/strong><\/p>\n<p>This is where we actually run our job.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\nif( postvalue(\"ajaxMail\") != false ){\r\n$response = array(\"totalSent\" =&gt; 0, \"errors\" =&gt; 0,\"mailed\" =&gt; 0);\r\n\r\n\/\/ number of records to process per step\r\n$recCount = postvalue(\"mail_per_step\");\r\n\r\n\/\/ starting record\r\n$startRecord = intval(postvalue(\"ajaxstep\"))*$recCount;\r\n\r\n\/\/ name of the email field\r\n$email_field = \"email\";\r\n\r\n\/\/ sql query\r\n$sql = \"select * from customers limit \".$startRecord.\",\".$recCount;\r\n$rs = DB::Query($sql);\r\n\r\n\/\/ the main cycle where we perform our tasks\r\n\r\nwhile($data = $rs-&gt;fetchAssoc()){\r\n$mail_params = array('to' =&gt; $data[$email_field], 'subject' =&gt; \"Ajax notification\", 'htmlbody' =&gt; \"body text\");\r\n\r\n$mailed = runner_mail($mail_params);\r\nif($mailed[\"mailed\"])\r\n$response[\"mailed\"]++;\r\nelse\r\n$response[\"errors\"]++;\r\n$response[\"totalSent\"]++;\r\n}\r\n\/\/ return the response\r\necho my_json_encode($response);\r\nexit();\r\n}<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nif (MVCFunctions.postvalue(new XVar(\"ajaxMail\")) != false) {\r\ndynamic data, rs, email_field = null, mail_params = null, mailed = XVar.Array(), recCount = null, sql = null, startRecord = null, var_response = XVar.Array();\r\nvar_response = XVar.Clone(new XVar(\"totalSent\", 0, \"errors\", 0, \"mailed\", 0));\r\nrecCount = XVar.Clone(MVCFunctions.postvalue(new XVar(\"mail_per_step\")));\r\nstartRecord = XVar.Clone((int) MVCFunctions.postvalue(new XVar(\"ajaxstep\")) * recCount);\r\nemail_field = new XVar(\"email\");\r\nsql = XVar.Clone(MVCFunctions.Concat(\"select * from customers limit \", startRecord, \",\", recCount));\r\nrs = XVar.Clone(DB.Query((XVar)(sql)));\r\nwhile (XVar.Pack(data = XVar.Clone(rs.fetchAssoc()))) {\r\nmail_params = XVar.Clone(new XVar(\"to\", data[email_field], \"subject\", \"Ajax notification\", \"htmlbody\", \"body text\"));\r\nmailed = XVar.Clone(MVCFunctions.runner_mail((XVar)(mail_params)));\r\nif (XVar.Pack(mailed[\"mailed\"])) {\r\nvar_response[\"mailed\"]++;\r\n} else {\r\nvar_response[\"errors\"]++;\r\n}\r\nvar_response[\"totalSent\"]++;\r\n}\r\nMVCFunctions.Echo(MVCFunctions.my_json_encode((XVar)(var_response)));\r\nMVCFunctions.ob_flush();\r\nHttpContext.Current.Response.End();\r\nthrow new RunnerInlineOutputException();\r\n}<\/textarea><\/pre>\n<\/div>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;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&#8230;<span class=\"clearfix clearfix-post\"><\/span><a href=\"https:\/\/xlinesoft.com\/blog\/2023\/08\/28\/long-running-process-with-notifications\/\" class=\"more-link\">Continue Reading <span class=\"screen-reader-text\">&#8220;Long running process with notifications&#8221;<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,95,1,8],"tags":[],"_links":{"self":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2989"}],"collection":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/comments?post=2989"}],"version-history":[{"count":26,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2989\/revisions"}],"predecessor-version":[{"id":3028,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/2989\/revisions\/3028"}],"wp:attachment":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/media?parent=2989"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/categories?post=2989"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/tags?post=2989"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}