Cómo rechazar correctamente en async/await? Análisis en TypeScript

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Async/Await y reject: ¿por qué es un dolor para los principiantes?

\n

¿Alguna vez ha escrito una función async, esperó una respuesta del servidor y luego se encontró con un UnhandledPromiseRejection? ¿Le suena familiar? Este es uno de los problemas más comunes que enfrentan los desarrolladores al migrar a async/await en TypeScript.

\n\n

En Stack Overflow, la pregunta "How to reject in async/await syntax?" ha acumulado miles de visitas. Y con razón. A primera vista, parece que basta con devolver Promise.reject() y listo. Pero en proyectos reales es más complejo: tipos, cadenas de llamadas, pérdida de contexto de errores.

\n\n

En esta guía, analizaremos cómo rechazar promesas correctamente en funciones async de TypeScript, aprenderemos a diferenciar throw de return Promise.reject() y veremos ejemplos prácticos de proyectos reales.

\n\n

Fundamentos: cómo funciona reject en async/await

\n

Antes de profundizar, recordemos los conceptos básicos. Cualquier función async en TypeScript devuelve un Promise. Si dentro de ella ocurre un throw o se devuelve Promise.reject(), la promesa pasa al estado rejected.

\n\n
// Ejemplo simple de reject en async/await\nasync function fetchUser(id: number): Promise<User> {\n    if (id <= 0) {\n        throw new Error('ID de usuario inválido');\n    }\n    // ...lógica para obtener el usuario\n}
\n\n

Aquí, throw se convierte automáticamente en un reject. Pero, ¿qué pasa si queremos devolver una promesa ya rechazada?

\n\n

throw vs return Promise.reject()

\n

En TypeScript, ambos enfoques funcionan, pero hay matices:

\n\n
    \n
  • throw — es la forma más idiomática. Detiene la ejecución de la función y pasa el control al catch más cercano.
  • \n
  • return Promise.reject() — devuelve explícitamente una promesa rechazada. Es útil cuando se necesita pasar un error desde otra función.
  • \n
\n\n
// Diferencia entre throw y return Promise.reject()\nasync function processOrder(orderId: string): Promise<OrderStatus> {\n    // Opción 1: throw\n    if (!orderId) {\n        throw new Error('Se requiere el ID del pedido');\n    }\n    \n    // Opción 2: return Promise.reject\n    if (orderId.startsWith('test_')) {\n        return Promise.reject(new Error('Los pedidos de prueba no están permitidos'));\n    }\n    \n    return { status: 'processing' };\n}
\n\n

Importante: TypeScript no siempre infiere correctamente los tipos con return Promise.reject(). Si la función espera Promise<T> y usted devuelve Promise.reject(), puede ocurrir un error de tipificación. En esos casos, es mejor usar throw.

\n\n

Errores típicos al hacer reject en funciones async

\n

Incluso los desarrolladores experimentados en TypeScript cometen errores clásicos. Aquí están los 3 principales:

\n\n

1. Olvidar manejar el reject en el código que llama

\n
async function main() {\n    // Mal: sin try/catch\n    const user = await fetchUser(-1);\n    console.log(user); // ¡UnhandledPromiseRejection!\n}\n\n// Bien:\nasync function mainSafe() {\n    try {\n        const user = await fetchUser(-1);\n        console.log(user);\n    } catch (error) {\n        console.error('Error al obtener el usuario:', error);\n    }\n}
\n\n

2. Tipificación incorrecta de errores

\n

En TypeScript, el bloque catch por defecto captura unknown. Esto es correcto, pero a veces los desarrolladores intentan acceder a error.message sin verificar.

\n\n
try {\n    await riskyOperation();\n} catch (error) {\n    // Error: error es unknown\n    console.log(error.message); // ❌ La propiedad 'message' no existe en el tipo 'unknown'\n    \n    // Correcto:\n    if (error instanceof Error) {\n        console.log(error.message);\n    } else {\n        console.log                

Blogs

Book Recommendations