Asp.net Best AJAX practice for client invocation of a slow async process on a server

I have a service that performs a slow task, when it is finished, I want to update the client using AJAX with the result of the task. In my client, I call the task many times to update the results grid. For purposes of understanding, this is a connection tester that iterates over the list of connections to see if they are alive.

I implemented the service as WCF. I generate async methods when I add a service link to my web client.

The code works fine, but the screen locks instantly when callbacks fire - I think this happens because they all happen one after another, and they all quickly redraw the GridView. I do not want this glitch to occur. I was hoping that the AJAX implementation would be able to partially update the GridView as the results are returned from the service via callbacks.

The only way I can do this is to start asynchronous calls in a separate client thread, and then use a timer to redraw the data to the grid (the same data that is updated in a separate thread through callbacks).

I am doing this mini-project as a training exercise, then I want to do the same with MVC3 to find out the differences.

A snippet of code (without a separate thread, which slows down the display of the screen during the callback):

//get list of connections from session ConnectionList myConns = Session[SESSION_ID] as ConnectionList; //pass into async service call GetAllStatusAsync(myConns); protected void GetAllStatusAsync(ConnectionList myConns) { Service1Client myClient = new WcfConnectionServiceRef.Service1Client(); myClient.AsyncWorkCompleted += new EventHandler<AsyncWorkCompletedEventArgs>(myClient_AsyncWorkCompleted); foreach (ConnectionDetail conn in myConns.ConnectionDetail) { //this call isnt blocking, conn wont be updated until later in the callback myClient.AsyncWorkAsync(conn); } } //callback method from async task void myClient_AsyncWorkCompleted(object sender, AsyncWorkCompletedEventArgs e) { ConnectionDetail connResult = e.Result; //get list of connections from session ConnectionList myConns = Session[SESSION_ID] as ConnectionList; //update our local store UpdateConnectionStore(connResult, myConns); //rebind grid BindConnectionDetailsToGrid(myConns); } 

Question: can this be done better in asp.net/AJAX? (In order to avoid problems with rendering blocking and partially get a mesh update as the results arrive), I really do not want to use a separate client stream, for example the following fragment:

  // Perform processing of files async in another thread so rendering is not slowed down // this is a fire and forget approach so i will never get results back unless i poll for them in timer from the main thread ThreadPool.QueueUserWorkItem(delegate { //get list of connections from session ConnectionList myConns = Session[SESSION_ID] as ConnectionList; //pass into async service call GetAllStatusAsync(myConns); }); 

UPDATE:

Adding page layout on demand:

 <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ASForm.aspx.cs" Inherits="Web_Asp_FBMonitor.ASForm" Async="true" EnableSessionState="True" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2> ASP.NET Connection Test (Client in ASYNC, Server in ASYNC) </h2> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <p> <%--This update panel shows the time, updated every second--%> &nbsp;<asp:UpdatePanel ID="UpdatePanel2" runat="server"> <ContentTemplate> <h3> <asp:Label ID="LabelTime" runat="server" Text=""></asp:Label> </h3> <asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick"> </asp:Timer> </ContentTemplate> </asp:UpdatePanel> </p> <p> <%--This update panel shows our results grid--%> &nbsp;<asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <asp:GridView ID="GridView1" runat="server"> </asp:GridView> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Client Sync Page</asp:HyperLink> <br /> <asp:Button ID="ButtonUpdate" runat="server" Text="Update" onclick="ButtonUpdate_Click" /> </ContentTemplate> </asp:UpdatePanel> </p> </asp:Content> 

UPDATE 2:

I am looking for a client side JS example. The received options are good and were highly appreciated, but the client JS is the one with which I am confused with my own inexperience and will reward this award.

+4
source share
4 answers

You see what you call โ€œscreen locksโ€ because of ASP UpdatePanel s.

ASP.NET WebForms is an attempt to make a web action like Windows forms. Amazing? Depends on who you ask.

When you use UpdatePanel , ASP.NET stores the server-side controls in the ViewState , makes the necessary changes to this ViewState (in your case, it updates the page based on the code in your Timer_Tick and ButtonUpdate_Click ). He then refreshes the page with this new data, causing a description of the screen locks.

To get around this, you have to use real AJAX. Many people do this using jQuery AJAX functions and one of the following options:

  • WebMethods ASP.NET
  • WCF Services
  • Separate ASP.NET Pages

Here are a lot of questions about connecting ASP.NET WebMethods through jQuery, and also about loading ASP.NET pages, but not so many questions about AJAX and WCF.

If you decide to use AJAX and jQuery, your final page will look something like this:

 <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ASForm.aspx.cs" Inherits="Web_Asp_FBMonitor.ASForm" Async="true" EnableSessionState="True" %> <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server"> <!-- include jQuery library here --> <script language="javascript" type="text/javascript"> $(document).ready(function () { UpdateGrid(); // Separate AJAX call to another page, WCF service, or ASP.NET WebMethod for the Timer results }); function UpdateGrid() { $.ajax({ url: "GridViewResults.aspx", done: function (result) { $("#gridResults").html(result.d); } }); } </script> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2> ASP.NET Connection Test (Client in ASYNC, Server in ASYNC) </h2> <p> <h3> <span id="timer"></span> </h3> </p> <p id="gridResults"> </p> <button id="ButtonUpdate" onclick="UpdateGrid();">Update</button> </asp:Content> 

Then, on a separate page (I conditionally called it GridViewResults.aspx above), you would get:

 <asp:GridView ID="GridView1" runat="server"></asp:GridView> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Client Sync Page</asp:HyperLink> <br /> 
+2
source

You might want to take a look at the async pages on ASP.net. This will allow you to make one AJAX callback and force the server to perform asynchronous polling on the server side. Then you can rebuild the grid when all tasks are returned asynchronously and you have a complete dataset.

Link to an article explaining thsi:

http://msdn.microsoft.com/en-us/magazine/cc163725.aspx


OK, based on the feedback from the comments, you want to update the grid as each update returns. I'm not sure how you send your AJAX requests from the browser, but you might want to look at them asynchronously from the client (something like jQuery might be useful here), and then use the script to redraw the specific grid line you need again when you will get the result.

+1
source

From the code you posted, it looks like you are making one call from the client to the server, and then the server makes a bunch of asynchronous calls. When the async results arrive, it updates the Grid, and when the last result arrives, it returns the updated grid to the client.

You may be able to fix this by forcing the server to return after the first asynchronous response is received. However, there are potential problems with cleaning by simply discarding outstanding requests. If you want to use this option, it will help if you can publish part of your server code that manages incoming requests.

If you want to receive updates in the grid as the results arrive on the server, there are several potentially cleaner alternatives:

  • Instead of making one call from the client, which fanates on several asynchronous calls on the server, it produces several asynchronous JS calls from the client with one synchronous call on the server. Upon completion of each synchronization call, it is returned to the client. After that, the client JS code will find and update the corresponding part of your grid.

  • Switch to using WebSockets. This is a bi-directional data connection. The server can periodically interrogate information using asynchronous requests, and then send the results to the client as they arrive. Then the client will use the script, as in No. 1 above, to update the grid.

  • Use a long survey. Have a background thread that periodically examines information and maintains data structures with the current state and either a serial number or a time stamp. When a client makes an Ajax request for an update, it passes the last timestamp or sequence number that it received. The server code then checks to see if there is anything new in the background stream's data structures. If so, it updates the grid and returns it. If not, it goes into sleep mode (waiting for a separate lock) until the background thread receives an update, and at that moment it signals a block, causing the request stream to wake up, refresh the page with the latest data and return.

Here is a sample code using jQuery to create an asynchronous Ajax call:

 $.get('myinfo.ashx', function(data) { $('.result').html(data); }) 

More details: http://api.jquery.com/jQuery.get/

+1
source

Have you considered using continuous polling / persistent join?

There is currently an excellent infrastructure to simplify its implementation in ASP.net called SignalR .

Here are some articles to get you started:

http://www.hanselman.com/blog/AsynchronousScalableWebApplicationsWithRealtimePersistentLongrunningConnectionsWithSignalR.aspx

http://www.amazedsaint.com/2011/11/introduction-ksigdo-knockout-signalr-to.html

For the problem you are describing, it sounds as if it can be very good, as it will allow the screen to be updated when and when data is transferred to the subscriber.

0
source

Source: https://habr.com/ru/post/1382545/


All Articles