Programación asíncrona en TypeScript: guía completa para principiantes
\n\nLa programación asíncrona es uno de los conceptos clave en el desarrollo web moderno. Si estás empezando a aprender TypeScript, comprender cómo trabajar con código asíncrono te abrirá las puertas para crear aplicaciones rápidas y receptivas. En esta guía, analizaremos los fundamentos de la programación asíncrona en TypeScript: desde simples callbacks hasta los modernos async/await, con énfasis en el tipado estricto que proporciona TypeScript.
¿Qué es la programación asíncrona y por qué es necesaria?
\n\nEn la programación síncrona, el código se ejecuta secuencialmente: cada operación siguiente espera a que finalice la anterior. Esto puede provocar que la interfaz se "congele" si, por ejemplo, es necesario cargar datos del servidor. La programación asíncrona permite ejecutar operaciones largas (solicitudes a API, lectura de archivos, temporizadores) en segundo plano, sin bloquear el hilo de ejecución principal. TypeScript, al ser una extensión de JavaScript, es totalmente compatible con los patrones asíncronos y añade un potente sistema de tipos.
\n\nPromise: la base de la asincronía en TypeScript
\n\nPromise es un objeto que representa el resultado de una operación asíncrona, que puede estar en uno de tres estados: pending (pendiente), fulfilled (completada con éxito) o rejected (completada con error). En TypeScript, podemos especificar el tipo de datos que devolverá la promesa, lo que hace que el código sea más predecible.
Ejemplo de creación de Promise con tipado:
\n\n// Función que devuelve una Promise de tipo string\nfunction fetchUserData(userId: number): Promise<string> {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n if (userId > 0) {\n resolve(`Datos del usuario con ID ${userId}`);\n } else {\n reject(new Error("ID de usuario no válido"));\n }\n }, 1000);\n });\n}\n\n// Uso de Promise\nfetchUserData(123)\n .then((data: string) => {\n console.log(data); // "Datos del usuario con ID 123"\n })\n .catch((error: Error) => {\n console.error(error.message);\n });\n\nObserva: hemos indicado explícitamente que la promesa devuelve string, y en .then() el parámetro data también tiene el tipo string. Esto ayuda a evitar errores en tiempo de compilación.
Cadenas de Promise y manejo de errores
\n\nLas promesas se pueden encadenar, pasando el resultado de una operación asíncrona a la siguiente. TypeScript inferirá automáticamente los tipos en cada paso.
\n\nEjemplo de cadena de Promise:
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction getUser(id: number): Promise<User> {\n return new Promise((resolve) => {\n setTimeout(() => resolve({ id, name: "Ana" }), 500);\n });\n}\n\nfunction getPosts(userId: number): Promise<string[]> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(["Post 1", "Post 2"]), 500);\n });\n}\n\n// Cadena de llamadas\ngetUser(1)\n .then((user: User) => {\n console.log(`Usuario: ${user.name}`);\n return getPosts(user.id); // Devolvemos una nueva Promise\n })\n .then((posts: string[]) => {\n console.log(`Posts: ${posts.join(", ")}`);\n })\n .catch((error: Error) => {\n console.error("Error:", error.message);\n });\n\nSi ocurre un error en cualquier etapa, la ejecución pasará al .catch() más cercano. Este manejo centralizado de errores es una de las principales ventajas de las promesas sobre los callbacks.
Async/await: sintaxis elegante para la asincronía
\n\nLa sintaxis async/await hace que el código asíncrono se parezca al síncrono, lo que facilita su lectura