En esta página

Promesas

15 min lectura TextoCap. 4 — Asincronía

Qué es una promesa?

Una promesa (Promise) es un objeto que representa el resultado futuro de una operación asincrona. Puede estar en uno de tres estados:

  • Pending — La operación aun no término
  • Fulfilled — La operación se completo con éxito (tiene un valor)
  • Rejected — La operación fallo (tiene un error)

Una vez que una promesa se resuelve o rechaza, su estado es inmutable: no puede cambiar.

Crear promesas

El constructor new Promise() recibe una función con dos callbacks:

const promesa = new Promise((resolve, reject) => {
  // operación asincrona...
  if (éxito) {
    resolve(resultado);  // transición a fulfilled
  } else {
    reject(new Error('algo fallo'));  // transición a rejected
  }
});

Consumir promesas

.then()

Recibe el valor cuando la promesa se resuelve. Retorna una nueva promesa, permitiendo encadenar:

promesa
  .then(valor => transformar(valor))
  .then(transformado => usarlo(transformado));

.catch()

Captura cualquier error en la cadena:

promesa
  .then(valor => procesarlo(valor))
  .catch(error => manejarError(error));

.finally()

Se ejecuta siempre, sin importar si la promesa se resolvio o rechazo. Es útil para limpiar recursos (cerrar spinners, liberar locks, etc.).

Encadenar promesas

Cada .then() retorna una nueva promesa. Si retornas un valor, la siguiente promesa se resuelve con ese valor. Si retornas otra promesa, la cadena espera a que se resuelva:

obtenerUsuario(1)
  .then(user => obtenerPerfil(user.id))
  .then(perfil => obtenerFotos(perfil.albumId))
  .then(fotos => mostrarGaleria(fotos))
  .catch(error => mostrarError(error));

Combinadores de promesas

JavaScript ofrece cuatro métodos estaticos para trabajar con multiples promesas simultaneamente:

Método Resuelve cuando... Rechaza cuando...
Promise.all Todas se resuelven Una falla
Promise.allSettled Todas terminan (éxito o fallo) Nunca
Promise.race La primera termina La primera falla
Promise.any La primera tiene éxito Todas fallan

Patron: timeout con Promise.race

Un patrón comun es competir una petición contra un timer para implementar timeouts:

function conTimeout(promesa, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
  return Promise.race([promesa, timeout]);
}

Errores comunes

  • Olvidar retornar en un .then() — La siguiente promesa recibe undefined
  • No manejar errores — Siempre agrega .catch() al final de la cadena
  • Crear promesas innecesarias — Si ya tienes una promesa, no la envuelvas en otra

Práctica

  1. Crea y consume una promesa: Escribe una funcion esperar(ms) que retorne una promesa que se resuelva despues de ms milisegundos. Consumela con .then() para imprimir un mensaje al resolverse.
  2. Encadena promesas con manejo de errores: Crea dos funciones que retornen promesas: obtenerUsuario(id) y obtenerPosts(userId). Encadenalas con .then() y agrega .catch() y .finally() para manejar errores y limpieza.
  3. Usa Promise.all y Promise.allSettled: Crea 3 promesas (una que falle intencionalmente) y ejecutalas con Promise.all y luego con Promise.allSettled. Compara los resultados y observa como cada combinador maneja el fallo.

En la siguiente leccion aprenderemos async/await, la forma moderna de trabajar con promesas.

Siempre maneja errores
Toda cadena de promesas debe terminar con .catch() para manejar errores. Una promesa rechazada sin catch causa un UnhandledPromiseRejection, que en Node.js termina el proceso.
Promise.allSettled vs Promise.all
Usa Promise.all cuando necesitas que TODAS las promesas tengan éxito. Usa Promise.allSettled cuando quieres los resultados de todas, incluso si algunas fallan (ej. cargar datos de multiples APIs independientes).
javascript
// Crear una promesa
function buscarUsuario(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, nombre: 'Carlos', email: '[email protected]' });
      } else {
        reject(new Error('ID invalido'));
      }
    }, 1000);
  });
}

// Consumir con .then/.catch/.finally
buscarUsuario(1)
  .then(usuario => {
    console.log('Usuario:', usuario.nombre);
    return usuario.email;
  })
  .then(email => {
    console.log('Email:', email);
  })
  .catch(error => {
    console.error('Error:', error.message);
  })
  .finally(() => {
    console.log('Busqueda finalizada');
  });

// Encadenar promesas (cada .then retorna una nueva promesa)
function obtenerPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, título: 'Primer post' },
        { id: 2, título: 'Segundo post' },
      ]);
    }, 500);
  });
}

buscarUsuario(1)
  .then(usuario => obtenerPosts(usuario.id))
  .then(posts => console.log('Posts:', posts))
  .catch(error => console.error(error));
javascript
// Simular peticiones asincronas con promesas
function obtenerUsuarios() {
  return new Promise((resolve) => {
    setTimeout(() => resolve([{ id: 1, nombre: 'Ana' }]), 1000);
  });
}

function obtenerPosts() {
  return new Promise((resolve) => {
    setTimeout(() => resolve([{ id: 1, titulo: 'Hola' }]), 1500);
  });
}

function obtenerComentarios() {
  return new Promise((resolve) => {
    setTimeout(() => resolve([{ id: 1, texto: 'Genial!' }]), 800);
  });
}

// Promise.all - espera a TODAS (falla si una falla)
Promise.all([obtenerUsuarios(), obtenerPosts(), obtenerComentarios()])
  .then(([usuarios, posts, comentarios]) => {
    console.log('Todos los datos:', usuarios, posts, comentarios);
  })
  .catch(error => {
    console.error('Una peticion fallo:', error);
  });

// Promise.allSettled - espera a todas (nunca falla)
Promise.allSettled([obtenerUsuarios(), obtenerPosts(), obtenerComentarios()])
  .then(resultados => {
    resultados.forEach(r => {
      if (r.status === 'fulfilled') {
        console.log('OK:', r.value);
      } else {
        console.log('Error:', r.reason);
      }
    });
  });

// Promise.race - retorna la primera que se resuelva/rechace
const timeout = new Promise((_, reject) => {
  setTimeout(() => reject(new Error('Timeout')), 5000);
});

Promise.race([obtenerUsuarios(), timeout])
  .then(data => console.log('Datos:', data))
  .catch(err => console.error(err.message));

// Promise.any - retorna la primera que se RESUELVA
Promise.any([obtenerUsuarios(), obtenerPosts(), obtenerComentarios()])
  .then(primera => console.log('Primera exitosa:', primera));