¿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?

avo
fuente
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);
}
Stephen Cleary
fuente
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.

Eric Lippert
fuente
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

Idrees Khan
fuente
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
5

Si usa ReSharper, una extensión recomendada gratuita podría serle útil. Analiza los métodos "async void" y resalta cuando se usan de manera inapropiada. La extensión puede distinguir diferentes usos de async void y proporcionar las correcciones rápidas apropiadas que se describen aquí: Wiki de extensión recomendada .

Alexander Zwitbaum
fuente