c# async-await

c# - ¿Por qué exactamente el vacío asíncrono es malo?



async-await (2)

Bueno, repasando las razones en el artículo msdn.microsoft.com/en-us/magazine/jj991977.aspx :

  • Los métodos de vacío asíncrono tienen diferentes semánticas de manejo de errores. Las excepciones que se escapen de PrimeCustomTask serán muy difíciles de manejar.
  • Los métodos de vacío asíncrono tienen una semántica de composición diferente. Este es un argumento centrado en la mantenibilidad y reutilización del código. Esencialmente, la lógica en PrimeCustomTask está ahí y eso es todo: no se puede componer en un método async nivel superior.
  • Los métodos de vacío asíncrono son difíciles de probar. Siguiendo naturalmente los primeros dos puntos, es muy difícil escribir una prueba unitaria que cubra PrimeCustomTask (o cualquier cosa que lo llame).

También es importante tener en cuenta que la async Task es el enfoque natural. De los varios lenguajes que han adoptado async / await , C # / VB son los únicos AFAIK que admiten async void . F # no, Python no, JavaScript y TypeScript no. async void no es natural desde la perspectiva del diseño del lenguaje.

La razón por la que se agregó el async void a C # / VB fue para habilitar controladores de eventos asíncronos. Si cambia su código para usar controladores de eventos anulados async void :

protected override async void OnLoad(EventArgs e) { if (CustomTask == null) await PrimeCustomTask(); } private async Task PrimeCustomTask()

Luego, las desventajas de async void se limitan a su controlador de eventos. En particular, las excepciones de PrimeCustomTask se propagan naturalmente a sus llamadores (asíncronos) ( OnLoad ), PrimeCustomTask puede componerse (llamado naturalmente desde otros métodos asincrónicos), y PrimeCustomTask es mucho más fácil de incluir en una prueba unitaria.

Entonces entiendo por qué regresar vacío de async normalmente no tendría sentido, pero me he encontrado con una situación en la que creo que sería perfectamente válido. Considere el siguiente ejemplo artificial:

protected override void OnLoad(EventArgs e) { if (CustomTask == null) // Do not await anything, let OnLoad return. PrimeCustomTask(); } private TaskCompletionSource<int> CustomTask; // I DO NOT care about the return value from this. So why is void bad? private async void PrimeCustomTask() { CustomTask = new TaskCompletionSource<int>(); int result = 0; try { // Wait for button click to set the value, but do not block the UI. result = await CustomTask.Task; } catch { // Handle exceptions } CustomTask = null; // Show the value MessageBox.Show(result.ToString()); } private void button1_Click(object sender, EventArgs e) { if (CustomTask != null) CustomTask.SetResult(500); }

Me doy cuenta de que este es un ejemplo inusual, pero traté de hacerlo simple y más generalizado. ¿Podría alguien explicarme por qué este es un código horrible y también cómo podría modificarlo para seguir las convenciones correctamente?

Gracias por cualquier ayuda.


El uso de void async solo se ve generalmente como "malo" porque:

  • No puedes esperar a que se complete (como ya se mencionó en esta publicación)
  • Cualquier excepción no controlada terminará su proceso (¡ay!)

Hay muchos casos (como el tuyo) donde usarlo está bien. Solo tenga cuidado al usarlo.