¿Cómo debugueo errores de async/await en TypeScript?

Christina Schmidt DE 🔍 Entusiasta 👁 87 ⚑ Denunciar Programación

Estoy trabajando en una API de Node.js con TypeScript y llevo un par de semanas teniendo problemas con async/await. Las Promises no se resuelven correctamente de alguna forma y recibo mensajes de error crípticos que realmente no me ayudan. ¿Cuál es la mejor forma de proceder al debuggear esto?

5 respuestas

★ Mejor respuesta

El problema generalmente no está en async/await en sí, sino en que no ves los errores correctamente, porque terminan en el scope equivocado o porque una Promise se rechaza silenciosamente en el trasfondo.

Me pasó hace poco: tenía una función que hacía varios API calls en paralelo, y uno falló. Al principio asumir que mi código con await estaba roto, pero al final resultó que simplemente no estaba registrando el error. Por eso: escribe `.catch()` en todos lados, incluso en Promises que "no necesitas", y registra agresivamente con `console.error()` o un Logger - dale al error todo el contexto. Eso ayuda mucho más que leer Stack Traces. Y siempre verifica si estás usando Promise.all() o Promise.race() en algún lado - esos también pueden engañarte si una Promise falla silenciosamente.

Los mensajes de error "crípticos" en realidad no son tan crípticos la mayoría de las veces - solo tienes que saber dónde mirar. Intenta escribir tus funciones async con bloques `.catch()` explícitos en lugar de solo try-catch, y loguea el stack de error completo, no solo el mensaje. También usa el debugger de Node.js o Chrome DevTools para recorrer tus Promises paso a paso - así ves de inmediato dónde una Promise se queda atascada o directamente no se resuelve. Otro error frecuente es olvidar `await` o iniciar Promises sin `await` accidentalmente; con `eslint-plugin-promise` atrapas esas cosas automáticamente.

Al hacer debug de async/await en TypeScript, el Stack Trace es tu mejor amigo, pero tienes que saber leerlo. Si una Promise se queda colgada sin resolverse, primero verifica si en algún lado olvidaste el `await`. Pasa más seguido de lo que parece: llamas una función async pero no esperas el resultado. También con `.map()` o `.forEach()` suele fallar porque los callbacks son async pero el map en sí no espera. Ahí te ayuda `Promise.all()` o `for...of` en lugar de métodos de Array.

Otro punto importante: revisa tu estrategia de manejo de errores. Si solo tienes try-catch y el Error Message sigue viéndose críptico, loguea el objeto Error completo, no solo el Message. `console.log(error)` en lugar de `console.log(error.message)` muchas veces muestra mucho más contexto. Y muy importante - en Node.js hay Promise Rejections sin manejar que simplemente mueren en el background. Activa el evento listener `process.on('unhandledRejection', ...)` para debuggear, así ves dónde realmente fallan las promises.

Un consejo práctico: usa TypeScript mismo como herramienta de debug. Si tus Type Annotations son correctas, el compilador de TypeScript ya te dirá si olvidaste awaitar algo - obtienes errores de tipo en lugar de sorpresas en runtime. Eso te ahorra tiempo de debug.

un truco práctico que uso: registra la hora y el contexto antes y después de cada await. así te das cuenta inmediatamente dónde se queda algo o tarda eternamente. `console.time('myFunc')` antes de la función async, luego `console.timeEnd('myFunc')` después. suena simple, pero muchas veces te muestra al tiro dónde está el cuello de botella - a veces son dependencias que tardan un montón, no tu código.

más importante aún: usa `Promise.all()` o `Promise.allSettled()` cuando ejecutas varias promises en paralelo. `allSettled` es oro puro, porque ves cuáles promises fallan y cuáles no - en lugar de que una se muera temprano e ignores el resto. con `allSettled` obtienes un objeto para cada promise con status y value/reason, ahí ves exactamente dónde se queda todo.

y un hack que muchos se pierden: agrega `"lib": ["es2020"]` (o más nueva) en tu tsconfig.json y usa optional chaining y nullish coalescing bien en los resultados de las promises. pero siendo honesto - a veces escribe `debugger;` directamente en las funciones async e inicia node con `--inspect`. luego abres chrome://inspect y debuggueas en vivo. ahí ves el stack completo y puedes ir paso a paso mirando dónde se quedan las promises. es más tedioso que los logs, pero si los logs no te ayudan, ese es el movimiento.

Olvídate de todos esos bloques `.catch()` junto con try-catch, solo hace las cosas más confusas - mantente con una estrategia. Lo que realmente ayuda: activa los eventos `unhandledRejection` en tu app de Node (`process.on('unhandledRejection', ...)`) y registra todo ahí, si no tus Promises se mueren en silencio. Luego escribe temporalmente un utilidad wrapper simple que envuelva cada función async con logging - antes del await anotas timestamp y función, después nuevamente, y ves inmediatamente si algo se queda atrapado o tarda demasiado. `console.time()` funciona, pero es demasiado granular para funciones grandes; unos cuantos `console.log('antes de await X')` estratégicamente colocados son útiles al principio.

Tu respuesta

Iniciar sesiónpara responder.