7

I have a Hub class, which has a long running method, I need to display a progress bar while it's working.

I read this article and I think it is possible to use IProgress interface in async methods to send long running operation status.

I write a method like this:

    public async Task<string> GetServerTime(IProgress<int> prog)
    {
        await Task.Run(() => {
            for (int i = 0; i < 10; i++)
            {
                prog.Report(i * 10);
                System.Threading.Thread.Sleep(200);
            }
        });

        return DateTime.Now.ToString();
    }

And I try to invoke the method like this:

 var appHub = $.connection.appHub;
 $.connection.hub.start();
 appHub.server.getServerTime()
              .done(function (time) {
                    alert(time);
               });

But I don't know how can I get the progress reports.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
Saeed Hamed
  • 732
  • 2
  • 10
  • 28
  • 1
    Hi, You have to create the instance on the IProgress on the client and give it to the getServerTime function. Then you register to the event on the IProgress on the client to do your stuff – bouh Oct 27 '14 at 10:19
  • Thanks, how can I create an instance on the client?! – Saeed Hamed Oct 27 '14 at 10:32

2 Answers2

12

You can use progress, as such:

var appHub = $.connection.appHub;
$.connection.hub.start().done(function() {
  appHub.server.getServerTime()
    .progress(function (update) { alert(update); })
    .done(function (time) { alert(time); });
});

On a side note, there's no point in using Task.Run over CPU-bound code on the server side. Your server-side code could just as easily be:

public string GetServerTime(IProgress<int> prog)
{
    for (int i = 0; i < 10; i++)
    {
        prog.Report(i * 10);
        System.Threading.Thread.Sleep(200);
    }

    return DateTime.Now.ToString();
}

Your server-side methods should only be async if they have true asynchronous work to do (usually I/O-bound operations). As a general rule, avoid Task.Run on the server side.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 2
    At first I got an error saying **progress** method is an undefined type, then I replaced **jquery 1.6.4**, which I was using, with **jquery 1.11.1**, so now it works! thanks @Stephen Cleary :) – Saeed Hamed Oct 28 '14 at 10:05
  • Same idea as @Stephen Cleary recommended in a sample by DamianEdwards/SignalR-2.x-demo https://github.com/DamianEdwards/SignalR-2.x-demo/blob/master/SignalR2x/Web/HubProgress/index.cshtml – Adi Bilauca Feb 05 '15 at 11:48
  • 2
    Just small note regarding using `async` on server in SignalR hubs. There is very clear guidance in [SignalR documentation](http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server#asyncmethods) about it: _Making a Hub method asynchronous avoids blocking the connection when it uses the WebSocket transport. When a Hub method executes synchronously and the transport is WebSocket, subsequent invocations of methods on the Hub from the same client are blocked until the Hub method completes._ – Michal Levý May 29 '15 at 15:10
0

In your javascript add the following client method

 var appHub = $.connection.appHub;
  $.connection.hub.start();
  appHub.client.progress = function(progresspct) {
     // logic for whatever you want to do.
  };

  appHub.server.getServerTime()
          .done(function (time) {
                alert(time);
           });

Modify your server side code to this -

    public async Task<string> GetServerTime(IProgress<int> prog)
   {
    await Task.Run(() => {
        for (int i = 0; i < 10; i++)
        {
            prog.Report(i * 10);
            // Call to your client side method.
            Clients.Client(Context.ConnectionId).progress(i);
            System.Threading.Thread.Sleep(200);
        }
    });

    return DateTime.Now.ToString();
}
Gjohn
  • 1,261
  • 1
  • 8
  • 12