{"id":39,"date":"2025-03-02T20:22:46","date_gmt":"2025-03-03T00:22:46","guid":{"rendered":"https:\/\/blog.jure.org.ve\/?p=39"},"modified":"2025-03-02T20:25:14","modified_gmt":"2025-03-03T00:25:14","slug":"programacion-asincrona-en-net-guia-practica-con-ejemplos-en-cataas","status":"publish","type":"post","link":"https:\/\/juredev.com\/blog\/2025\/03\/programacion-asincrona-en-net-guia-practica-con-ejemplos-en-cataas\/","title":{"rendered":"Programaci\u00f3n As\u00edncrona en .NET: Gu\u00eda Pr\u00e1ctica con Ejemplos en CATAAS"},"content":{"rendered":"\n<p>Hace poco revisaba un viejo programa que escrib\u00ed hace tiempo y me di cuenta de que no usaba asincron\u00eda en ninguna parte; al verlo, not\u00e9 cu\u00e1nto ha cambiado el desarrollo en estos d\u00edas. Hoy en d\u00eda, la programaci\u00f3n as\u00edncrona en C# es fundamental para construir aplicaciones con buena capacidad de respuesta en diversos escenarios de desarrollo, permitiendo que tareas largas, como solicitudes de red o lecturas de archivos, se ejecuten sin interrumpir la experiencia del usuario. Sin embargo, si no se usa bien, puede traer problemas de rendimiento, como un consumo innecesario de recursos, bloqueos que paralizan la aplicaci\u00f3n o comportamientos inesperados, como excepciones perdidas o resultados que no cuadran.<\/p>\n\n\n\n<p>Por eso, entender y aplicar correctamente patrones como async\/await es clave para sacarle provecho y evitar dolores de cabeza comunes en el desarrollo. A trav\u00e9s de la API de <a href=\"https:\/\/cataas.com\/\"><strong>CATAAS<\/strong> (Cat as a Service)<\/a>, voy a explicarte en este art\u00edculo con varios ejemplos c\u00f3mo usar buenas pr\u00e1cticas cuando escribas c\u00f3digo que incluya async\/await en .NET. Empecemos:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Usar <code>async Task<\/code> en lugar de <code>async void<\/code><\/h2>\n\n\n\n<p>Al empezar a aprender sobre la asincron\u00eda en C#, una de las primeras lecciones que aprend\u00ed fue la diferencia entre <strong>async Task<\/strong> y <strong>async void<\/strong>, y en verdad, fue un cambio de juego. Utilizar <strong>async Task<\/strong> en lugar de <strong>async void <\/strong>es fundamental porque permite que a quien llame al m\u00e9todo pueda esperar a que todo termine sin perder el control. Por ejemplo, si estoy haciendo una llamada HTTP a la API de CATAAS para traer una imagen de un gato, quiero estar seguro de que, si algo falla, como la red que se cae o la API que no responde, el error se capture y maneje bien, no que se quede en el limbo. Con <strong>async Task<\/strong>, tenemos un flujo de ejecuci\u00f3n m\u00e1s predecible y robusto, y eso me da tranquilidad sabiendo que mi c\u00f3digo no va a explotar silenciosamente en producci\u00f3n. En cambio, con <strong>async void<\/strong>, es como jugar a la ruleta: si algo sale mal, no hay forma f\u00e1cil de enterarse, y eso es un riesgo que no quieres correr.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task GetCatImageAsync()\n{\n    var response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\");\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2. Propagar la asincron\u00eda en toda la cadena<\/h2>\n\n\n\n<p>Algo que me cost\u00f3 entender al principio, y que ahora veo como muy importante, es propagar la asincron\u00eda por toda la cadena de m\u00e9todos. Hace un tiempo, mezclaba c\u00f3digo <em>s\u00edncrono<\/em> y as\u00edncrono sin pensarlo mucho, y terminaba con bloqueos que me hac\u00edan rascar la cabeza pregunt\u00e1ndome qu\u00e9 hab\u00eda salido mal. Mantener la asincron\u00eda a lo largo de toda la pila de llamadas evita esos problemas, como los temidos <em>deadlocks<\/em> que paralizan todo, y hace que el c\u00f3digo fluya de manera natural. Por ejemplo, si estoy llamando a mi m\u00e9todo anterior <strong>GetCatImageAsync()<\/strong> desde otro lugar, no basta con lanzarlo y olvidarme; necesito usar <strong>await<\/strong> ah\u00ed tambi\u00e9n, y luego en el m\u00e9todo que lo llama, y as\u00ed sucesivamente. De esta forma, cada paso que espera una operaci\u00f3n as\u00edncrona, como traer una imagen de gato de CATAAS, usa <strong>await<\/strong>, asegurando un flujo de trabajo coherente y eficiente que no se traba ni me deja esperando eternamente. Es como mantener una conversaci\u00f3n fluida: todos tienen que estar en sinton\u00eda, o alguien se queda colgado.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task DisplayCatImageAsync()\n{\n    await FetchCatImageAsync(); \/\/ Propaga el await\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. Operaciones paralelas con Task.WhenAll<\/h2>\n\n\n\n<p>Lo que me encanta de <em>Async\/Await<\/em> es poder ejecutar tareas simult\u00e1neamente con <strong>Task.WhenAll<\/strong>. Antes, cuando intentaba obtener im\u00e1genes de gatos con diferentes etiquetas de CATAAS secuencialmente, me preguntaba si era posible mejorar el c\u00f3digo. Ahora, con <strong>Task.WhenAll,<\/strong> lanzo m\u00faltiples tareas a la vez, como tener varios ayudantes trabajando en paralelo. Puedo pedir, por ejemplo, un gato \u00abcute\u00bb, otro \u00abfunny\u00bb y uno \u00abgrumpy\u00bb simult\u00e1neamente, esperando que todos se completen juntos. Esto reduce el tiempo de ejecuci\u00f3n al aprovechar el paralelismo en operaciones independientes, mejorando la eficiencia de mis aplicaciones, es como si hiciera malabares con varias pelotas a la vez.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task FetchMultipleCatsAsync(IEnumerable&lt;string> catTags)\n{\n    var tasks = catTags.Select(tag => FetchCatWithTagAsync(tag));\n    await Task.WhenAll(tasks);\n}\n<\/code><\/pre>\n\n\n\n<p>Al trabajar con tareas paralelas, es crucial prestar atenci\u00f3n al manejo de errores. Inicialmente, subestim\u00e9 su importancia, pero aprend\u00ed que un solo fallo, como un tag inv\u00e1lido en la API, puede comprometer todo el conjunto de tareas. Por ello, es fundamental implementar un robusto manejo de excepciones. Esto asegura que, si una tarea falla, las dem\u00e1s puedan continuar sin problemas, evitando que toda la aplicaci\u00f3n se vea afectada por un \u00fanico error. M\u00e1s adelante, profundizaremos en c\u00f3mo implementar estas estrategias de manejo de errores de manera efectiva.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Evitar bloqueos con <code>.Result<\/code> o <code>.Wait()<\/code><\/h2>\n\n\n\n<p>Ojo con los m\u00e9todos <strong>.Result<\/strong> o <strong>.Wait()<\/strong>: bloquean el hilo actual hasta que el <strong>Task<\/strong> termine. Si el <strong>Task<\/strong> necesita volver al mismo hilo, pero \u00e9ste est\u00e1 ocupado esperando, se produce un <em>deadlock<\/em>, \u00abun nadie cede y todo se traba\u00bb. Era como decirle al hilo: \u00abNo te muevas de aqu\u00ed hasta que acabe\u00bb , y en apps con interfaz, todo se congelaba, complicando el manejo de errores. En cambio, con <strong>await<\/strong>, libero el hilo mientras espero la respuesta de la API, como una foto de un gato adorable, permitiendo que la app siga funcionando. Esto no solo mantiene la fluidez para el usuario, sino que mejora la escalabilidad del c\u00f3digo, al no comprometer recursos innecesariamente.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task GetCatImageDataAsync()\n{\n   var response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\");\n   var content = await response.Content.ReadAsByteArrayAsync();\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. Manejar excepciones con <code>try\/catch<\/code><\/h2>\n\n\n\n<p>Si hay algo que me ha dado dolores cabeza m\u00e1s de una vez mientras hacia codificaba operaciones as\u00edncronas, es aprender a usar <em>try\/catch<\/em> como se debe. Ya sabes que si una llamada HTTP a CATAAS fallaba, quiz\u00e1 por una URL mal escrita o la red tuvo una ca\u00eda inesperada,mi aplicaci\u00f3n se estrellaba sin darme ni una pista de qu\u00e9 pas\u00f3. Incorporar bloques <em>try\/catc<\/em>h en operaciones as\u00edncronas me permite capturar y manejar errores espec\u00edficos, como esos que salen cuando la API devuelve un error o simplemente no responde. Ahora, en lugar de que todo colapse, puedo atrapar el problema, como un \u00ab404 Not Found\u00bb de un gato que no existe, y decidir qu\u00e9 hacer: mostrar un mensaje amistoso, reintentar la solicitud o sencillamente saltarme ese paso sin que el resto de mi programa se detenga en seco. Eso hace que el sistema sea m\u00e1s listo y resistente, y me evita esas fallas incomodas cuando algo falla en plena ejecuci\u00f3n.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nConsole.WriteLine(\"Ejemplo 5: Intentando obtener imagen de gato...\");\ntry\n{\n   var response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\/invalid\"); \/\/ URL inv\u00e1lida\n   response.EnsureSuccessStatusCode();\n   Console.WriteLine(\"\u00a1Gato obtenido con \u00e9xito!\");\n}\ncatch (HttpRequestException ex)\n{\n    Console.WriteLine($\"Error al obtener el gato: {ex.Message}\");\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6. Cancelaci\u00f3n con <code>CancellationToken<\/code><\/h2>\n\n\n\n<p>Al profundizar en la asincron\u00eda, hubo algo que me hizo darme cuenta de lo mucho que pod\u00eda controlar las cosas: el <strong>CancellationToken<\/strong>. Hasta ahora hemos dejado que las solicitudes a CATAAS se ejecuten hasta el final, como por ejemplo, pidi\u00e9ndole una imagen de gato que a veces tardaba una eternidad por una red lenta, y la aplicaci\u00f3n se quedaba ah\u00ed, esperando sin fin, como si el tiempo no importara. Implementar <strong>CancellationToken<\/strong> en operaciones largas nos da control: ahora podemos ponerle un <em>timeout<\/em> y decirle a una solicitud \u201coye mijo, si no terminas en dos segundos, olv\u00eddate\u201d. Esto es un salvavidas en interfaces de usuario o servicios en tiempo real, donde no queremos que el usuario se quede mirando una pantalla congelada o que un <em>backend<\/em> se atasque por algo que no vale la pena esperar. Con un <strong>CancellationToken<\/strong>, le damos a nuestro c\u00f3digo la flexibilidad de cancelar esas solicitudes que demoran demasiado, asegur\u00e1ndome de que la aplicaci\u00f3n siga viva y responda, sin quedarse atrapada en un espera interminable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nusing(var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2))) \n{\n    try \n    {\n        var response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\", cts.Token);\n        Console.WriteLine(\"\u00a1Gato obtenido antes de que se cancele!\");\n    } \n    catch (TaskCanceledException) \n    {\n        Console.WriteLine(\"La solicitud fue cancelada.\");\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7. Optimizar con cach\u00e9 para operaciones frecuentes<\/h2>\n\n\n\n<p>Me di cuenta de que ciertas operaciones en mi c\u00f3digo se repet\u00edan demasiado, en este caso, como pedir la misma imagen de gato a CATAAS una y otra vez. Pens\u00e9: \u00abDebe haber una forma m\u00e1s inteligente de hacer esto\u00bb. Ah\u00ed es cuando el uso de cach\u00e9 se volvi\u00f3 una excelente soluci\u00f3n. Guardar los resultados de operaciones frecuentes evita llamadas redundantes a servicios externos. La diferencia es notable: menos espera, menos latencia y un rendimiento visiblemente m\u00e1s r\u00e1pido. Por ejemplo, si ya tengo una imagen en memoria, \u00bfpara qu\u00e9 volver a pedirla a la API? Es como tener una caja de herramientas a mano en lugar de ir a la ferreter\u00eda cada vez que necesito un destornillador. Esta estrategia me permite optimizar recursos y mejorar significativamente la eficiencia de mi aplicaci\u00f3n.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nprivate static byte&#91;]? cachedCatImage = null;\n\npublic static async Task GetCachedCatImageAsync()\n{\n    if (cachedCatImage != null) return;\n    cachedCatImage = await HttpClient.GetByteArrayAsync(\"https:\/\/cataas.com\/cat\");\n}\n<\/code><\/pre>\n\n\n\n<p>Eso s\u00ed, toma en cuenta que en entornos reales, digamos, una app en producci\u00f3n, no basta con guardar y listo; agregar mecanismos de expiraci\u00f3n o invalidaci\u00f3n es clave para que la informaci\u00f3n no se quede vieja, como asegurarme de que mi gato en cach\u00e9 no sea de hace tres meses cuando CATAAS ya tiene uno nuevo esper\u00e1ndome.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. Nombres descriptivos para m\u00e9todos as\u00edncronos<\/h2>\n\n\n\n<p>Las convenciones de nomenclatura para m\u00e9todos as\u00edncronos son cruciales cuando trabajas en equipo. Adoptar el sufijo \u00ab<em>Async<\/em>\u00ab, como en <strong>GetCatImageAsync<\/strong> para CATAAS, debe una pr\u00e1ctica habitual cuando nombras un m\u00e9todo, ayudando a mi memoria en el futuro y a quienes revisan mi c\u00f3digo. Esto no solo facilita encontrar y mantener el c\u00f3digo, sino que indica: \u00ab\u00a1Tengo la impresi\u00f3n que este m\u00e9todo es as\u00edncrono!\u00bb. Evita confusiones, especialmente cuando alguien, o yo mismo meses despu\u00e9s, necesita entender r\u00e1pidamente qu\u00e9 sucede. Por ejemplo, <strong>DownloadCatImageAsync<\/strong> indica de inmediato que no es instant\u00e1neo y probablemente requiera <strong>await<\/strong>, mientras que nombrarlo simplemente como <strong>DownloadCatImage<\/strong> podr\u00eda enga\u00f1arme haci\u00e9ndome pensar que es s\u00edncrono. Es como etiquetar claramente una caja: te ahorras abrirla para saber su contenido.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task DownloadCatImageAsync() { ... }\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9. Uso de <code>ConfigureAwait(false)<\/code> cuando sea apropiado.<\/h2>\n\n\n\n<p>Sigamos con algo que al principio, <strong>ConfigureAwait(false)<\/strong>, me parec\u00eda uno de esos trucos raros de programadores avanzados que no val\u00eda la pena entender, con el tiempo descubr\u00ed que tiene un encanto especial. Imagina que el hilo es como un repartidor de pizzas: normalmente, despu\u00e9s de entregar un pedido, tiene que volver a la pizzer\u00eda (el contexto original) para tomar el siguiente. Usar <strong>ConfigureAwait(false)<\/strong> es como decirle: \u00abOye, no hace falta que regreses a la pizzer\u00eda cada vez; entrega el pr\u00f3ximo pedido desde donde est\u00e9s\u00bb. En c\u00f3digo como servicios <em>backend<\/em> o librer\u00edas, donde no hay una interfaz de usuario esperando actualizaciones, esto hace que todo sea m\u00e1s r\u00e1pido y evita esos enredos horribles llamados <em>deadlocks<\/em>. Ahora, cuando escribo algo que no necesita el contexto de la UI, pongo <strong>ConfigureAwait(false)<\/strong> y me parece que todo va m\u00e1s fluido, sin dar vueltas innecesarias.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nvar response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\").ConfigureAwait(false);\nvar content = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);\nConsole.WriteLine($\"Imagen obtenida: {content.Length} bytes\");\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">10. Evitar <code><strong>async\/await<\/strong><\/code> innecesario<\/h2>\n\n\n\n<p>Si un m\u00e9todo solo devuelve un valor inmediato, como una cadena o un n\u00famero, y no realiza operaciones as\u00edncronas reales, toma en cuenta que <strong>Task.FromResult<\/strong> es la soluci\u00f3n perfecta. Es como decirle al c\u00f3digo: \u201cNo te compliques, solo dame el resultado\u201d. Por ejemplo, para devolver la URL base de CATAAS sin tocar la red, <strong>Task.FromResult<\/strong> evita la sobrecarga de <strong>async\/await<\/strong>, manteniendo el c\u00f3digo m\u00e1s limpio y eficiente.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static Task&lt;string> GetCatUrlBetterAsync()\n{\n    return Task.FromResult(\"https:\/\/cataas.com\/cat\");\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">11. Probar el c\u00f3digo as\u00edncrono adecuadamente<\/h2>\n\n\n\n<p>Cuando llegue a este punto en este proyecto con CATAAS, me pregunt\u00e9: \u00ab\u00bfC\u00f3mo puedo hacer una prueba simple para comprobar que funciona como espero?\u00bb. Ah\u00ed se me ocurri\u00f3 incluir una prueba b\u00e1sica directamente en el c\u00f3digo, como un experimento casero. Esto me permiti\u00f3 validar si una imagen de gato de CATAAS llegaba correctamente o fallaba como deber\u00eda, comprobando que mi operaci\u00f3n as\u00edncrona se ejecutaba bien. Es un ejemplo perfecto: escribes un m\u00e9todo, lo ejecutas, ves el resultado en la consola y \u00a1listo! Sabes si vas por buen camino.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\npublic static async Task TestDownloadCatImageAsync()\n{\n    Console.WriteLine(\"Ejemplo 11: Probando la descarga de imagen de gato...\");\n    var response = await HttpClient.GetAsync(\"https:\/\/cataas.com\/cat\");\n    var content = await response.Content.ReadAsByteArrayAsync();\n    if (content.Length > 0)\n    {\n        Console.WriteLine(\"Prueba exitosa: La imagen no est\u00e1 vac\u00eda.\");\n    }\n    else\n    {\n        Console.WriteLine(\"Prueba fallida: La imagen est\u00e1 vac\u00eda.\");\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>Aunque me encanta esta simplicidad para validaciones r\u00e1pidas sin necesidad de un entorno de pruebas completo, en producci\u00f3n recomiendo usar frameworks como <strong>xUnit<\/strong> o <strong>NUnit<\/strong> para cubrir escenarios m\u00e1s complejos. Esto es algo que planeo explorar en un futuro art\u00edculo para mostrarte ese mundo en profundidad.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n\n\n<p>Dominar la programaci\u00f3n as\u00edncrona es esencial en el desarrollo moderno. Este art\u00edculo, sin pretender ser una Biblia sobre el tema, aspiro te ha guie a trav\u00e9s de pr\u00e1cticas clave y \u00fatiles como el uso efectivo de <strong>await<\/strong>, el manejo de errores con <em>try\/catch<\/em> y la optimizaci\u00f3n con cach\u00e9, todo ejemplificado con la API de CATAAS. Ahora tienes las herramientas para escribir c\u00f3digo m\u00e1s eficiente y mantenible, evitando los errores comunes. Te invito a explorar el   <a href=\"https:\/\/github.com\/jure-ve\/AsyncAwaitBestPracticesWithCataas\">repositorio<\/a> para ver estos conceptos en acci\u00f3n.<\/p>\n\n\n\n<p>La asincron\u00eda abre un mundo de posibilidades para aplicaciones m\u00e1s r\u00e1pidas y responsivas. Este es solo el comienzo. \u00a1Sigue codificando y explorando las profundidades de .NET!. Sigamos codificando.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hace poco revisaba un viejo programa que escrib\u00ed hace tiempo y me di cuenta de que no usaba asincron\u00eda en ninguna parte; al verlo, not\u00e9 cu\u00e1nto ha cambiado el desarrollo en estos d\u00edas. Hoy en d\u00eda, la programaci\u00f3n as\u00edncrona en C# es fundamental para construir aplicaciones con buena capacidad de respuesta en diversos escenarios de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[11],"class_list":["post-39","post","type-post","status-publish","format-standard","hentry","category-desarrollo","tag-c"],"_links":{"self":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts\/39","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/comments?post=39"}],"version-history":[{"count":0,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/posts\/39\/revisions"}],"wp:attachment":[{"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/media?parent=39"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/categories?post=39"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/juredev.com\/blog\/wp-json\/wp\/v2\/tags?post=39"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}