¿Debo evitar los controladores de eventos 'async void'?


119

Sé que generalmente se considera una mala idea usar async voidmétodos de disparar y olvidar para iniciar tareas, porque no hay un seguimiento de la tarea pendiente y es complicado manejar las excepciones que pueden aparecer dentro de dicho método.

¿Debo evitar generalmente los async voidcontroladores de eventos también? Por ejemplo,

private async void Form_Load(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Puedo reescribirlo así:

Task onFormLoadTask = null; // track the task, can implement cancellation

private void Form_Load(object sender, System.EventArgs e)
{
        this.onFormLoadTask = OnFormLoadTaskAsync(sender, e);
} 

private async Task OnFormLoadTaskAsync(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

¿Cuáles son las rocas submarinas para los controladores de eventos asíncronos, además de la posible reentrada?


Deberías, pero no puedes. Además de eso, todos los cuidados que debe tomar al usar async void ya son requeridos por los controladores de eventos de la interfaz de usuario.
Paulo Morgado

Y la reentrada ocurre debido a operaciones asincrónicas activadas por el controlador de eventos y no por el uso de async-await por sí mismo.
Paulo Morgado

Respuestas:


153

La pauta es evitar, async void excepto cuando se usa en un controlador de eventos, por lo que usarlo async voiden un controlador de eventos está bien.

Dicho esto, por razones de pruebas unitarias , a menudo me gusta factorizar la lógica de todos los async voidmétodos. P.ej,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
  await Task.Delay(2000);
  ...
}

private async void Form_Load(object sender, EventArgs e)
{
  await OnFormLoadAsync(sender, e);
}

Tengo curiosidad ... ¿hay alguna razón por la que no cambie Form_Loadel acceso a public? Parece que el código sería menos detallado de esa manera.
InteXX

Vaya, no importa ... VBer tratando de leer C # aquí ... Acabo de notar el tipo de retorno de OnFormLoadAsync. Ahora veo que esto lo convierte en un truco útil. Gracias.
InteXX

Dicho todo esto, ¿podrías echar un vistazo y dar una opinión aquí ? ¡Gracias!
InteXX

2
@ AlexHopeO'Connor: La Handledbandera debe establecerse sincrónicamente; no se puede utilizar asyncpara tomar una decisión sobre si el evento se gestiona o no.
Stephen Cleary

2
@ AlexHopeO'Connor: Ha pasado un tiempo desde que trabajé con una aplicación WPF, pero he usado soluciones similares a esa en el pasado. Es decir, haz el ICommand.Executemétodo async void; Considero esto aceptable ya ICommand.Executeque lógicamente es un controlador de eventos.
Stephen Cleary

50

¿Debería evitar generalmente los controladores de eventos vacíos asíncronos también?

Generalmente, los controladores de eventos son el único caso en el que un método asincrónico vacío no es un olor a código potencial.

Ahora bien, si necesita realizar un seguimiento de la tarea por alguna razón, entonces la técnica que describe es perfectamente razonable.


6

Sí, generalmente el vacío asíncrono de controladores de eventos es el único caso. Si quieres saber más al respecto, puedes ver un gran video aquí en el canal 9.

The only case where this kind of fire-and-forget is appropriate is in top-level event-handlers. Every other async method in your code should return "async Task".

aquí está el enlace


Los ' controladores de eventos de nivel superior ' son una pista importante. Cuando se usa el controlador de eventos vacío asíncrono en un controlador de eventos de nivel inferior, puede causar grandes problemas con excepciones no detectadas.
Portikus

Gracias por el enlace del video, muy útil
lsp

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.