Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

10 años online 🎂

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 15 de julio de 2014
image[2]La revisión 2.1 de SignalR ha incluido otra característica que puede ser interesante en determinados escenarios: la posibilidad de notificar al cliente del progreso de la ejecución de una acción invocada en el hub.

Aunque en los métodos que normalmente implementamos no tiene demasiada utilidad, sí puede ayudar a mejorar la interacción y experiencia de usuario cuando se trate de ejecutar métodos costosos como el siguiente:
public class GodsOfTheForest: Hub
{
    [...]
    public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything()
    {
        for (var i = 0; i < 10; i++)
        {
            await Task.Delay(1000);
            // Do something very important here
  }
        return 42;
    }
}
Hasta ahora, si desde el cliente llamábamos a este método del hub, sólo podíamos tomar el control cuando la llamaba finalizaba, bien obteniendo el resultado si todo había ido bien, o bien capturando el error producido. Por ejemplo, en Javascript lo conseguíamos con un código como el siguiente:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
    .done(function (result) {
        console.log("The answer is: " + result);
    })
    .fail(function(err) {
        console.log("Oops, something went wrong: " + err.message);
    });

Notificación de progreso en SignalR 2.1

Para notificar el progreso de la ejecución en un método del hub, a partir de SignalR 2.1 tenemos que hacer básicamente dos cosas:
  • En primer lugar, añadir al método del hub un parámetro de entrada adicional de tipo IProgress<T>, que obligatoriamente debe ser el último del método. T es el tipo de dato que vayamos a utilizar para notificar el progreso al cliente; por ejemplo, si vamos a notificar un porcentaje, podríamos usar Progress<int>.
  • En segundo lugar, en el punto del código desde donde queremos realizar la notificación, invocar al método Report() del parámetro anterior suministrándole el valor de progreso deseado.
Pero seguro que queda más claro si lo vemos con un poco de código basándonos en el ejemplo anterior:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
           IProgress<int> progress)
{
    for (var i = 1; i <= 100; i++)
    {
        await Task.Delay(1000);
        progress.Report(i);
    }
    return 42;
}
De esta forma tan simple estaremos enviando al cliente el progreso de la operación :-) Por supuesto, podríamos enviar al cliente notificaciones de cualquier tipo, como en el siguiente ejemplo, donde usamos IProgress<string> para poder notificar con mensajes de texto:
public async Task<int> GetTheAnswerToLifeTheUniverseAndEverything(
                            IProgress<string> progress)
{
    progress.Report("Just starting...");
    await Task.Delay(1000);
    progress.Report("Working hard...");
    await Task.Delay(1000);
    progress.Report("Almost done...");
    await Task.Delay(1000);
    progress.Report("One second to finish...");
    await Task.Delay(1000);
    progress.Report("Finished");
    return 42;
}

Consumo desde clientes del hub

El consumo de los datos de progreso sería bastante simple desde nuestro cliente Javascript, simplemente usar este método progress() para especificar la función callback que recibirá los datos de progreso:
proxy.server.getTheAnswerToLifeTheUniverseAndEverything()
    .progress(function (value) {
        log("Progress: " + value + "%");
    })
    .done(function (result) {
        log("The answer is: " + result);
    });
Ah, y un detalle muy importante a este respecto: el uso de esta característica requiere la versión 1.7 o superior de jQuery, más que nada porque en el objeto Deferred que retornan las llamadas a las funciones del hub antes no existía el método progress() que estamos usando para recibir las notificaciones de progreso.

Desde clientes .NET es igualmente sencillo. Se han añadido sobrecargas al método Invoke() de forma que podemos indicar el código a ejecutar al recibir el progreso de la operación. El siguiente código muestra una llamada a un método que retorna un entero y cómo gestionar las notificaciones de progreso que nos llegan también en forma de número entero:
int result = await proxy.Invoke<int, int>(
                    "GetTheAnswerToLifeTheUniverseAndEverything", 
                    (int progress) => 
                            Debug.WriteLine("Progress: " + progress + "%")
             );
El primer parámetro genérico del método Invoke()es el tipo de datos que retornará la llamada al hub, como ha sido siempre hasta ahora, mientras que el segundo es el utilizado para notificar el progreso. Así, si los mensajes de progreso nos llegan en forma de cadena de caracteres, deberíamos usar:
int result = await proxy.Invoke<int, string>(
                    "GetTheAnswerToLifeTheUniverseAndEverything", 
                    (string progress) => Debug.WriteLine(progress)
             );
Bueno, pues con este post podemos dar por cerrada la serie sobre las novedades de SignalR 2.1. Hay algunas más, pero creo que no son tan interesantes como las que hemos ido viendo; si tenéis interés en conocerlas, podéis acudir al repositorio de Github de SignalR.

Publicado en Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

4 Comentarios:

Rafa Osuna dijo...

Muy grande la 'Guía del autoestopista galáctico'.

Por otra parte, muy útil para la interacción con el cliente.

José M. Aguilar dijo...

Muy grande!!

Gracias, Rafa!

Vladimir Kelman dijo...

Hi Jose,
In your SignalR book, Chapter 4 EchoConnection gives connection.error if run application under local IIS instead of IIS Express. (I'm on Win 7 / IIS7.5 machine) Why could it be?

Thank you

José M. Aguilar dijo...

Hi, Vladimir!

All the examples should work in your local IIS, although it has some limitations in the number of concurrent connections allowed.

Take a look at your browser's development tools console, probably you'll find some clues there.

Regards,
JM