Introducción a la programación asíncrona en TypeScript
El desarrollo web moderno es impensable sin la programación asíncrona. Ya sean solicitudes a API, trabajo con el sistema de archivos o temporizadores, todas estas operaciones requieren una ejecución de código no bloqueante. TypeScript, como superconjunto de JavaScript, proporciona herramientas potentes para trabajar con asincronía, añadiendo tipado estricto y una legibilidad del código mejorada.
En esta guía, analizaremos en detalle todos los aspectos de la programación asíncrona en TypeScript: desde los Promise básicos hasta patrones avanzados con async/await y la ejecución paralela de tareas.
1. Fundamentos de Promise en TypeScript
¿Qué es una Promise?
Una Promise (promesa) es un objeto que representa el resultado de una operación asíncrona. Puede estar en uno de tres estados:
- Pending (pendiente) — estado inicial, la operación aún no ha finalizado.
- Fulfilled (cumplida) — la operación se completó con éxito.
- Rejected (rechazada) — la operación finalizó con un error.
Creación de Promise con tipado
TypeScript permite tipar estrictamente los valores devueltos por una Promise. Veamos un ejemplo:
// Función que devuelve una Promise con un tipo de datofunction fetchUserData(userId: number): Promise<User> { return new Promise<User>((resolve, reject) => { setTimeout(() => { const user: User = { id: userId, name: 'Iván Ivanov', email: 'ivan@example.com' }; if (userId > 0) { resolve(user); } else { reject(new Error('ID de usuario no válido')); } }, 1000); });}
// Interfaz para el tipado de datosinterface User { id: number; name: string; email: string;}
// Uso de PromisefetchUserData(1) .then((user: User) => { console.log(`Usuario: ${user.name}`); }) .catch((error: Error) => { console.error(`Error: ${error.message}`); });Cadenas de Promise (Chaining)
Las Promise se pueden encadenar para la ejecución secuencial de operaciones asíncronas:
function getUserPosts(userId: number): Promise<string[]> { return new Promise((resolve) => { setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 500); });}
function getPostComments(postTitle: string): Promise<string[]> { return new Promise((resolve) => { setTimeout(() => resolve(['Comentario 1', 'Comentario 2']), 300); });}
// Cadena de PromisefetchUserData(1) .then((user) => { console.log(`Usuario encontrado: ${user.name}`); return getUserPosts(user.id); }) .then((posts) => { console.log(`Publicaciones encontradas: ${posts.length}`); return getPostComments(posts[0]); }) .then((comments) => { console.log(`Comentarios en la primera publicación: ${comments.length}`); }) .catch((error) => { console.error('Ocurrió un error:', error); });2. Async/Await — enfoque moderno
Sintaxis de async/await
Las palabras clave async y await hacen que el código asíncrono se parezca al síncrono, lo que mejora significativamente su legibilidad. Una función marcada con async siempre devuelve una Promise.
// Función asíncrona con tipado del valor de retornoasync function loadUserProfile(userId: number): Promise<UserProfile> { try { const user = await fetchUserData(userId); const posts = await getUserPosts(userId); return { user, posts, lastLogin: new Date() };