Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display the Asynchronous results which one is first in asp.netweb application?

I have to send three asynch requests in three class files, 3 requests response times are different, first one is 2sec and second one is 7sec and third one is 4sec , now i have to display first response in browser with in 2sec and after 2sec display the third response and finally display the second response , but now my results all responses display at a time after completed three responses, please give me any suggestion, it is very urgent, please.....

my code is

public delegate string AsyncMethodCaller(string name);
        public delegate string AsyncMethodCallertest(string name);
        public delegate string NatilusAsyn(string name);

button click event

     AsyncMethodCaller caller = new AsyncMethodCaller(ps.PennStarService);
        IAsyncResult result = caller.BeginInvoke(txtFirst.Text, null, null);
    NatilusAsyn caller123 = new NatilusAsyn(cs.PennStarService);
        IAsyncResult result123 = caller123 .BeginInvoke(txtthird.Text, null, null);
        AsyncMethodCallertest cltest = new AsyncMethodCallertest(ps.testHi);
        IAsyncResult tetsresult = cltest.BeginInvoke(txtSecond.Text, null, null);
        lblFirst.Text = caller.EndInvoke(result);           
        lblSecond.Text = cltest.EndInvoke(tetsresult);
     lblthird.Text = caller123.EndInvoke(result123); 

thank u hemanth

like image 246
hmk Avatar asked Jun 18 '12 10:06

hmk


1 Answers

I think the root of the problem is to understand what happens with an ASP.Net request.

Each page has its life-cycle which includes a pipeline of events, for more information check this answer. Each request is handled by a worker thread from the AppDomain of the current application. The response won't be sent to the user until the page pipeline is completed.

About threads:

The number of simultaneous available threads can be configured in the machine.config. The key point to understand is that these number of threads are fixed, which means that when the max number of simultaneous threads has been reached, subsequent requests will be placed in a queue, the number of requests that can be placed in the queue are only limited by the memory of the server.

When a worker thread calls a long time-consuming operation, you are blocking that thread, until the operation finishes, which means again, if there are several concurrent users, potentially all available threads could be blocked, forcing new requests to be placed in a queue and in the worst case, causing a 503 error - Service Unavailable.

The way to prevent this is by calling these kind of methods in background threads. Similar to your code, but the behavior is not what you are expecting in this case, your code is waiting for the threads to end in order to finish the page request that's the reason you are receiving the result at the same time (at the end of the request).

For more details about executing asp.net pages asynchronously, refer to this excellent article

Now in order to get the results you need:

(This is a full working example, in this example, I am using Rx - Reactive Programming to run long time-consuming methods in a new thread, you can change this to use another framework if you like, I prefer using Rx because I feel more comfortable with the API)

Using PageMethods

Code behind

    [WebMethod]
    public static string Execute1()
    {
        JavaScriptSerializer j = new JavaScriptSerializer();
        string r = string.Empty;
        var o = Observable.Start(() =>
            {
                Thread.Sleep(2000);
                r = "My Name1: " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
            }, Scheduler.NewThread);
        o.First();
        r += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        r = j.Serialize(new { res = r });
        return r;
    }

    [WebMethod]
    public static string Execute2()
    {
        JavaScriptSerializer j = new JavaScriptSerializer();
        string r = string.Empty;
        var o = Observable.Start(() =>
        {
            Thread.Sleep(7000);
            r = "My Name2: " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        }, Scheduler.NewThread);
        o.First();
        r += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        r = j.Serialize(new { res = r });
        return r;
    }

    [WebMethod]
    public static string Execute3()
    {
        JavaScriptSerializer j = new JavaScriptSerializer();
        string r = string.Empty;
        var o = Observable.Start(() =>
        {
            Thread.Sleep(4000);
            r = "My Name3: " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        }, Scheduler.NewThread);
        o.First();
        r += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        r = j.Serialize(new { res = r });
        return r;
    }

ASPX

....
<script type="text/javascript" src="Scripts/jquery-1.7.2.min.js"></script>
....
<asp:ScriptManager runat="server" />
<input type="button" id="callAsync" name="callAsync" value="Call Async" />
<div id="first"></div>
<script type="text/javascript">
    function onsuccess1(msg) {
        var result = Sys.Serialization.JavaScriptSerializer.deserialize(msg.d);
        var h = $("#first").html();
        $("#first").html( h + "<br/>&nbsp;&nbsp;&nbsp;&nbsp;Result: " + result.res);
    }
    function onerror1(xhr) {
        alert(xhr.responseText);
    }
    function callMyMethod(url, mydata, onsuccess, onerror) {
        var h = $("#first").html();
        $("#first").html(h + "<br/>&nbsp;&nbsp;Calling Method: " + new Date());
        return $.ajax({
            cache: false,
            type: "POST",
            async: true,
            url: url,
            data: mydata, 
            contentType: "application/json",
            dataType: "json",
            success: function (msg) {
                onsuccess(msg);
            },
            error: function (xhr) {
                onerror(xhr);
            }
        });
    }
    $(document).ready(function () {
        $("#callAsync").click(function () {
            var h = $("#first").html();
            $("#first").html(h + "<br/>New call: " + new Date());
            callMyMethod("DynamicControls.aspx/Execute1", "{}", function (data) { onsuccess1(data); }, function (data) { onerror1(data); });
            callMyMethod("DynamicControls.aspx/Execute2", "{}", function (data) { onsuccess1(data); }, function (data) { onerror1(data); });
            callMyMethod("DynamicControls.aspx/Execute3", "{}", function (data) { onsuccess1(data); }, function (data) { onerror1(data); });
        });
    });
</script>

Output

This code generates the following:

New call: Fri Jun 22 02:11:17 CDT 2012
  Calling Method: Fri Jun 22 02:11:17 CDT 2012
  Calling Method: Fri Jun 22 02:11:17 CDT 2012
  Calling Method: Fri Jun 22 02:11:17 CDT 2012
    Result: My Name1: 6/22/2012 2:11:19 AM Background thread: 38 Main thread: 48
    Result: My Name2: 6/22/2012 2:11:26 AM Background thread: 50 Main thread: 48
    Result: My Name3: 6/22/2012 2:11:30 AM Background thread: 52 Main thread: 48

As you can see the code is not optimized, the main thread is being locked, the max number of seconds we set up are 7 but in this case, from the time the server code was called (02:11:17) until the last response received (2:11:30) 13 seconds elapsed. This is because ASP.Net by default locks the current Session object locking the main thread. Since we are not using the session in this example, we can configure the page like this:

<%@ Page EnableSessionState="False"

And the new output is:

New call: Fri Jun 22 02:13:43 CDT 2012
  Calling Method: Fri Jun 22 02:13:43 CDT 2012
  Calling Method: Fri Jun 22 02:13:43 CDT 2012
  Calling Method: Fri Jun 22 02:13:43 CDT 2012
    Result: My Name1: 6/22/2012 2:13:45 AM Background thread: 52 Main thread: 26
    Result: My Name3: 6/22/2012 2:13:47 AM Background thread: 38 Main thread: 49
    Result: My Name2: 6/22/2012 2:13:50 AM Background thread: 50 Main thread: 51

Now, only 7 seconds elapsed from the first server method call to the last response received without locking the main thread =)

Edit 1

If my understanding is correct, you need to pass some parameters to the methods and return datasets to fill labels and textboxes.

To pass parameters to the PageMethods, this is a way to do it:

Install these Nuget Package:

  • https://nuget.org/packages/json2
  • https://nuget.org/packages/Newtonsoft.Json
  • https://nuget.org/packages/jQuery
  • https://nuget.org/packages/Rx-Main/1.0.11226

Command class. This class will represent the parameters you want to send to the server method in the post action

public class ProcessXmlFilesCommand
{
    public string XmlFilePath { get; set; }

    public ProcessXmlFilesCommand()
    {
    }
}

Instead of returning a DataSet, I think it would be easier and more manageable to return an IEnumerable, something like this:

    [WebMethod]
    public static IEnumerable<MyResult> ProcessXmlFiles(ProcessXmlFilesCommand command)
    {
        List<MyResult> results = new List<MyResult>();
        var o = Observable.Start(() =>
        {
            // here do something useful, process your xml files for example
            // use the properties of the parameter command
            results.Add(new MyResult { SomethingInteresting = DateTime.Now.ToString(), FilePath = command.XmlFilePath + "Processed" });
            results.Add(new MyResult { SomethingInteresting = DateTime.Now.ToString(), FilePath = command.XmlFilePath + "Processed" });
            results.Add(new MyResult { SomethingInteresting = DateTime.Now.ToString(), FilePath = command.XmlFilePath + "Processed" });
            Thread.Sleep(2000);
        }, Scheduler.NewThread);
        o.First();
        return results.AsEnumerable();
    }

The MyResult class represents the data you want to send back to the user

public class MyResult
{
    public string SomethingInteresting { get; set; }
    public string FilePath { get; set; }
}

In your ASPX page

<script type="text/javascript" src="Scripts/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="Scripts/json2.min.js"></script>


    <script type="text/javascript">
        function processFiles() {
            var filePath = $("#filePath").val();
            var command = new processFilesCommand(filePath);
            var jsonCommand = JSON.stringify(command);

            $.ajax({
                cache: false,
                type: "POST",
                async: true,
                url: "CustomThreads.aspx/ProcessXmlFiles",
                data: "{'command':" + jsonCommand + "}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (msg) {
                    onFileProcessedSuccess(msg);
                },
                error: function (exc) {
                    onFileProcessedError(exc);
                }
            });
        }
        function onFileProcessedSuccess(msg) {
            var response = msg.d;
            $.each(response, function (index, myResponse) {
                $("#<%:this.myLabel.ClientID %>").append(myResponse.SomethingInteresting + "<br/>");
            });
        }
        function onFileProcessedError(exc) {
            alert("Error: " + exc.responseText);
        }
        function processFilesCommand(filePath) {
            this.XmlFilePath = filePath;
        }
        $(document).ready(function () {
            $("#processFile").click(processFiles);
        });
    </script>

<input type="text" name="filePath" id="filePath" />
<input type="button" name="processFile" id="processFile" value="Process File" />

<br /><asp:Label ID="myLabel" runat="server" />

Edit 2

This is a simplified way

<%@ Page EnableSessionState="False" Language="C#" AutoEventWireup="true" CodeBehind="CustomThreadsSimple.aspx.cs" Inherits="WebApplication1.CustomThreadsSimple" %>
....
<script type="text/javascript" src="Scripts/jquery-1.7.2.min.js"></script>
....
    <script type="text/javascript">
        function makeCall(url, data) {
            $("#<%:this.lblMessage.ClientID %>").append("<br/>Initializing call: " + new Date());
            $.ajax({
                url: url,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8;",
                async: true,
                cache: false,
                data: "{name:'" + data + "'}",
                success: function (msg) {
                    $("#<%:this.lblMessage.ClientID %>").append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;" + msg.d);
                },
                error: function (exc) {
                    alert(exc.responseText);
                }
            });
        }
        $(function () {
            $("#startProcess").click(function () {
                makeCall("CustomThreadsSimple.aspx/Execute1", $("#<%: this.txtData1.ClientID %>").val());
                makeCall("CustomThreadsSimple.aspx/Execute2", $("#<%: this.txtData2.ClientID %>").val());
                makeCall("CustomThreadsSimple.aspx/Execute3", $("#<%: this.txtData3.ClientID %>").val());
            });
        });
    </script>
    <asp:TextBox runat="server" ID="txtData1" />
    <asp:TextBox runat="server" ID="txtData2" />
    <asp:TextBox runat="server" ID="txtData3" />
    <input type="button" name="startProcess" id="startProcess" value="Start execution" />
    <asp:Label ID="lblMessage" runat="server" />

Code behind

public partial class CustomThreadsSimple : System.Web.UI.Page
{
    [WebMethod]
    public static string Execute1(string name)
    {
        string res = string.Empty;
        Func<string, string> asyncMethod = x =>
        {
            Thread.Sleep(2000);
            return "Res1: " + x +" " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        };

        IAsyncResult asyncRes = asyncMethod.BeginInvoke(name, null, null);
        res = asyncMethod.EndInvoke(asyncRes);
        res += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        return res;
    }

    [WebMethod]
    public static string Execute2(string name)
    {
        string res = string.Empty;
        Func<string, string> asyncMethod = x =>
        {
            Thread.Sleep(7000);
            return "Res2: " + x + " " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        };

        IAsyncResult asyncRes = asyncMethod.BeginInvoke(name, null, null);
        res = asyncMethod.EndInvoke(asyncRes);
        res += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        return res;
    }

    [WebMethod]
    public static string Execute3(string name)
    {
        string res = string.Empty;
        Func<string, string> asyncMethod = x =>
        {
            Thread.Sleep(4000);
            return "Res3: " + x + " " + DateTime.Now.ToString() + " Background thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        };

        IAsyncResult asyncRes = asyncMethod.BeginInvoke(name, null, null);
        res = asyncMethod.EndInvoke(asyncRes);
        res += " Main thread: " + Thread.CurrentThread.ManagedThreadId.ToString();
        return res;
    }
}

Output

enter image description here

like image 80
Jupaol Avatar answered Nov 12 '22 08:11

Jupaol