5:45 0 0
Blazor Task VS void

Blazor Task VS void

  DrUalcman |  septiembre 282022

Hace unos días estaba trabajando en un proyecto Blazor WebAssembly y comprobé algo muy curioso referente al uso de void y de Task en los métodos manejadores de eventos. Cuando necesitamos manejar un evento como un click lo manejamos con un método void, salvo que estemos realizando algún tipo de llamada asíncrona que es mejor utilizar Task. Pero he ahí el objetivo de este artículo hoy.

Blazor, Server o WebAssembly, trabajan de forma asíncrona, y si tenemos muchos componentes, objetos para manejar sesión y estado, es mejor trabajar to el proyecto de froma asíncrona. Por lo que cuando necesitamos un manejador de evento, es mejor que el método sea una tarea, de esta manera se controlará mejor el estado de los componentes. Esto es lo que pude percibir cuando trabaja en el projecto en cuestión.

Pero hay veces que, ya sea por error, por costumbre o, simplemente, por un cambio en el codigo a posteriory. Resulta que teníamos un manejador de evento que eata un método void, y ahora hace una llamada asíncrona y para ello sólo lo decoramos como async void. Es aquí donde nuestra aplicación ya no se comporta del todo como estábamos esperando, ya que la página no se actualiza y fuerza a la utilización del comando StateHasChanged(). Por lo que la recomendación es que siempre que se ejecute un método de froma asíncrona en Blazor el método, relacionado con el disparador del evento, sea un Task de esta froma minimizaremos el uso de StateHasChanged().

Un poco más tecnico y con ejemplos

En Blazor WebAssembly, los métodos Task se utilizan para representar una operación asíncrona que puede tardar algún tiempo en completarse, mientras que los métodos void se utilizan para representar una operación síncrona que se completa inmediatamente. Los métodos Task son más eficientes cuando se realizan operaciones que requieren tiempo, ya que permiten que el hilo principal de la aplicación siga ejecutando otras tareas mientras se espera a que la operación asíncrona se complete. 

Un ejemplo simple de un método Task

public async Task DoSomethingAsync()
{
// Some async operation here
await DoLongThingsAsync();
}

Este método DoSomethingAsync representa una operación asíncrona que no devuelve un valor. El async antes de la definición del método indica que se va a utilizar una operación asíncrona en el cuerpo del método. El Task como tipo de retorno indica que la operación es asíncrona y devuelve una tarea que se completará en algún momento en el futuro.

Un ejemplo simple de un método Void

public void DoSomething()
{
// Some synchronous operation here
}

Este método DoSomething representa una operación síncrona que no devuelve un valor. El método se completará inmediatamente después de que termine la operación.

Pero, ¿cual sería mas eficiente de utilizar?

Los métodos Task son más eficientes que los métodos void en Web Assembly cuando se realizan operaciones que requieren tiempo. Esto se debe a que los métodos Task permiten que el hilo principal de la aplicación siga ejecutando otras tareas mientras se espera a que la operación asíncrona se complete. En contraste, los métodos void bloquean el hilo principal hasta que se complete la operación, lo que puede afectar negativamente el rendimiento de la aplicación.

Un escenario en el que un método Task puede ser más eficiente que un método void es cuando se necesitan realizar múltiples tareas de manera asíncrona y paralela. En tales casos, el uso de un método Task permite que las tareas se ejecuten en segundo plano mientras el hilo principal de la aplicación puede continuar procesando otras tareas. En comparación, el uso de un método void puede bloquear el hilo principal y afectar el rendimiento general de la aplicación.

Suponga que tiene un componente Blazor que necesita descargar varios archivos de un servidor remoto cuando se hace clic en un botón. Para descargar los archivos, puede utilizar el servicio HttpClient y su método GetStreamAsync, que devuelve una tarea que se completará cuando se descargue el archivo. Suponga que desea descargar 10 archivos en paralelo y actualizar el estado de la interfaz de usuario después de que se hayan descargado todos los archivos.

Aquí hay una implementación utilizando un método Task:

public async Task DownloadFilesAsync()
{
var tasks = new List();

for (int i = 1; i <= 10; i++)
{
tasks.Add(httpClient.GetStreamAsync($"https://example.com/file{i}.txt"));
}

await Task.WhenAll(tasks);

// Actualizar el estado de la interfaz de usuario
stateHasChanged();
}

En este ejemplo, se utilizan múltiples tareas asíncronas para descargar los archivos de forma paralela. La llamada a Task.WhenAll espera a que todas las tareas se completen antes de continuar con la actualización del estado de la interfaz de usuario. 

Ahora, aquí hay una implementación utilizando un método void:

public void DownloadFiles()
{
for (int i = 1; i <= 10; i++)
{
httpClient.GetStreamAsync($"https://example.com/file{i}.txt");
}

// Actualizar el estado de la interfaz de usuario
stateHasChanged();
}

En este ejemplo, se utilizan llamadas a GetStreamAsync dentro de un bucle para descargar los archivos de forma secuencial. Después de que se inician todas las descargas, se actualiza el estado de la interfaz de usuario. Sin embargo, esto bloquea el hilo principal de la aplicación mientras se descargan los archivos, lo que puede afectar el rendimiento general de la aplicación.

¿Porque entonces mi problema de como empezó el blog? La respuesta en plan más tecnica de el porqué pasa esto

Es posible que esto se deba a que la llamada a StateHasChanged no se está realizando correctamente en el contexto de sincronización correcto. En Blazor, las actualizaciones de la interfaz de usuario deben realizarse en el contexto de sincronización de Blazor, que es el contexto en el que se ejecuta la aplicación. Si llama a StateHasChanged desde un contexto incorrecto, como un subproceso en segundo plano o un controlador de eventos onClick en un componente secundario, es posible que la actualización de la interfaz de usuario no se realice correctamente.

Cuando utiliza un método Task, Blazor administra automáticamente el contexto de sincronización correctamente, lo que garantiza que la actualización de la interfaz de usuario se realice correctamente. En cambio, cuando utiliza un método void, es posible que deba administrar manualmente el contexto de sincronización utilizando InvokeAsync o StateHasChanged.

Ultima recomendación

Como punto final comentar que es mejor siempre utilizar InvokeAsync(StateHasChanged); cuando queramos refrescar la interfaz de usurioa para que así Blazor maneje correctamente el contexto de la aplicación y refresque la página.

Happy coding :D

0 Comentarios

 
 
 

Archivo