Antes de las promesas de JavaScript, las operaciones como las solicitudes de red o la lectura de archivos se manejaban utilizando callbacks, lo que podía llevar a un código confuso y anidado (también conocido como «callback hell»).

Veamos, entonces, qué son las promesas y cómo pueden mejorar tu código, de una manera más sencilla y elegante.

    << [Comienza ya] Gestiona tu contenido de una manera fácil con el software  CMS gratuito de HubSpot>>   

 

En otras palabras, proporcionan una forma más estructurada y legible de manejar operaciones asíncronas, permitiendo que el código sea más claro y más fácil de mantener al escribir acciones de JavaScript.

Para qué sirven las promesas en JavaScript

Las promesas en JavaScript sirven para manejar operaciones asíncronas de manera más estructurada y legible. Cuando realizas tareas que no se completan de inmediato, como llamadas a API, lecturas de archivos o consultas a bases de datos, JavaScript necesita una forma de gestionar estas operaciones sin bloquear la ejecución del código.

Las promesas cumplen varias funciones clave:

Manejo del flujo asíncrono óptimo

Te permiten escribir un código más limpio y comprensible al evitar el anidamiento excesivo de callbacks. En lugar de anidar múltiples callbacks, puedes encadenar métodos .then() de manera más legible.

Gestión de errores

Brindan un mecanismo más efectivo para manejar errores en operaciones asíncronas. Puedes usar el método .catch() para capturar errores que ocurren en cualquier parte de la cadena de promesas.

Encadenamiento de operaciones

Permiten encadenar múltiples operaciones asíncronas de una manera más intuitiva. El resultado de una promesa puede ser pasado como entrada a la siguiente promesa en la cadena utilizando el método .then().

Manejo de paralelismo

Puedes utilizar constructores como Promise.all() y Promise.race() para manejar múltiples promesas al mismo tiempo. El primero espera a que todas las promesas se cumplan antes de continuar, mientras que el segundo se resuelve tan pronto como una de las promesas se cumple.

Legibilidad y mantenibilidad

Las promesas hacen que el código asíncrono sea más fácil de leer y mantener, ya que proporcionan una estructura clara para manejar resultados exitosos y errores, en lugar de tener que rastrear flujos de control complejos en callbacks anidados.

Compatibilidad con async/await

Asimismo, forman la base de las funciones async y await, una característica introducida en versiones más recientes de JavaScript. async/await simplifica aún más el manejo de código asíncrono al hacer que parezca código síncrono, mejorando la legibilidad.

Estados posibles de una promesa en JavaScript

Una promesa tiene tres estados posibles:

Pendiente (pending)

El estado «Pendiente» (pending) es uno de los tres posibles estados en los que una promesa puede encontrarse en JavaScript. Una promesa está en estado pendiente cuando se crea, pero la operación asíncrona asociada aún no ha sido completada ni ha fallado. En otras palabras, la promesa está en espera de que la operación asíncrona se resuelva o se rechace.

Durante el estado «Pendiente», la promesa está en una especie de limbo. Aunque la operación asincrónica está en curso, la promesa misma no tiene acceso directo al resultado final ni al error que pueda ocurrir. Sin embargo, la promesa se mantiene receptiva a su futuro estado cumplido (fulfilled) o rechazado (rejected).

Cuando la operación asíncrona se completa satisfactoriamente, la promesa pasa del estado «Pendiente» al estado «Cumplido» (fulfilled). En este punto, el valor resultante de la operación se convierte en el resultado de la promesa y puede ser accedido mediante el método .then().

Por otro lado, si la operación asíncrona encuentra un error, la promesa cambia de estado «Pendiente» al estado «Rechazado» (rejected). En este caso, se llama al método reject() en la definición de la promesa o en cualquier lugar donde se rechace explícitamente, y el error proporcionado se convierte en el motivo del rechazo. El manejo de errores se realiza comúnmente usando el método .catch() o utilizando bloques try/catch en combinación con async/await.

Cumplida (fulfilled)

Una promesa entra en el estado «Cumplida» cuando la operación asíncrona asociada se ha completado con éxito y ha producido un valor resultante.

En este estado, la promesa contiene el valor resultante de la operación asíncrona. Esto significa que la promesa ha cumplido su propósito y tiene un resultado con el que trabajar. Este valor puede ser cualquier tipo de dato, como un número, una cadena de texto, un objeto o incluso otra promesa. El valor cumplido se pasa como argumento a la función resolve() cuando se crea la promesa.

Para acceder al valor cumplido de una promesa, se utiliza el método .then() y se proporciona una función que se ejecutará una vez que la promesa esté en el estado «Cumplida».

Es importante destacar que una vez que una promesa está en el estado «Cumplida» no puede cambiar a otro. Esto significa que si una promesa se cumple con un valor, permanecerá en ese estado y no se puede volver a «Pendiente» ni cambiar a «Rechazada».

Rechazada (rejected)

Una promesa entra en el estado «Rechazada» cuando la operación asíncrona asociada ha fallado y se ha producido un error.

En este estado, la promesa contiene información sobre el motivo del fallo, es decir, el error que ocurrió durante la operación asíncrona. El motivo del rechazo se pasa como argumento a la función reject() cuando se crea la promesa.

Para manejar el estado «Rechazada» y el motivo del rechazo, se utiliza el método .catch() o se pueden manejar errores con bloques try/catch en combinación con async/await.

Estructura básica de una promesa en JavaScript

La estructura es la siguiente:


const myPromise = new Promise((resolve, reject) => {
  // Aquí se realiza la operación asíncrona
  // Si la operación es exitosa, se llama a resolve(valor)
  // Si ocurre un error, se llama a reject(error)
});

myPromise
  .then(resultado => {
    // Manejar el resultado si la promesa se cumple
  })
  .catch(error => {
    // Manejar el error si la promesa es rechazada
  });

La función resolve se utiliza para cumplir la promesa con un valor, mientras que la función reject se utiliza para rechazar la promesa con un error.

Las promesas también permiten encadenar múltiples operaciones asíncronas usando el método .then(), lo que hace que el código sea más legible y evita el anidamiento excesivo.

Nota: como hemos dicho, en versiones más recientes de JavaScript (a partir de ES6), se introdujeron async/await, que proporcionan una forma aún más cómoda de trabajar con código asíncrono basado en promesas, haciendo que parezca que se está trabajando con código síncrono, pero detrás de escena sigue siendo asincrónico.

Cómo implementar las promesas en JavaScript: ejemplos

Ejemplo de transacción de pago en línea

Supongamos que deseamos simular una transacción de pago en línea y queremos asegurarnos de que se manejen correctamente tanto los pagos exitosos como los fallidos, utilizando promesas.


// Simulación de una función que procesa un pago
function procesarPago(total) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const exito = Math.random() < 0.8; // Simulamos que el 80 % de las veces el pago es exitoso

      if (exito) {
        resolve("Pago exitoso");
      } else {
        reject(new Error("Error en el pago"));
      }
    }, 1500); // Simulamos un retraso de 1.5 segundos para el proceso de pago
  });
}

// Ejemplo de uso de la función de procesamiento de pagos
const totalAPagar = 100; // Monto total del pago

procesarPago(totalAPagar)
  .then(resultado => {
    console.log(resultado); // Se ejecutará en caso de pago exitoso
    // Aquí podríamos realizar acciones adicionales, como actualizar la base de datos, enviar un recibo por correo, etc.
  })
  .catch(error => {
    console.error(error.message); // Se ejecutará en caso de pago fallido
    // Aquí podemos ofrecer al usuario la opción de intentar nuevamente o mostrar un mensaje de error
  });

En este ejemplo, la función procesarPago() simula una transacción de pago en línea. Dependiendo de un valor aleatorio, la función decide si el pago es exitoso o fallido. La promesa que se devuelve se cumple si el pago es exitoso (el 80 % de las veces) y se rechaza si el pago falla.

Luego, utilizamos .then() para manejar el caso de pago exitoso, donde podemos realizar acciones adicionales, como actualizar la base de datos o enviar un recibo por correo electrónico. Empleamos .catch() para manejar el caso de pago fallido, donde es posible ofrecer al usuario opciones para resolver el problema o mostrar un mensaje de error.

Ejemplo de base de datos

En este caso, supondremos que estamos trabajando con datos de usuarios y queremos simular la adición de múltiples usuarios a una base de datos, teniendo en cuenta casos exitosos y fallidos.


// Simulación de una función que agrega usuarios a una base de datos
function agregarUsuarios(usuarios) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const exito = Math.random() < 0.7; // Simulamos que el 70 % de las veces la operación es exitosa

      if (exito) {
        resolve("Usuarios agregados exitosamente");
      } else {
        reject(new Error("Error al agregar usuarios"));
      }
    }, 2000); // Simulamos un retraso de 2 segundos para la operación de almacenamiento
  });
}

// Datos de ejemplo de usuarios a agregar a la base de datos
const nuevosUsuarios = [
  { id: 1, nombre: "Usuario 1" },
  { id: 2, nombre: "Usuario 2" },
  { id: 3, nombre: "Usuario 3" }
];

// Uso de la función de agregación de usuarios
agregarUsuarios(nuevosUsuarios)
  .then(resultado => {
    console.log(resultado); // Se ejecutará en caso de operación exitosa
    // Aquí podríamos realizar acciones adicionales, como actualizar la interfaz de usuario o notificar al usuario
  })
  .catch(error => {
    console.error(error.message); // Se ejecutará en caso de operación fallida
    // Aquí podríamos ofrecer al usuario la opción de intentar nuevamente o mostrar un mensaje de error
  });
  • Definición de la función agregarUsuarios(): esta función simula el proceso de agregar usuarios a una base de datos. Devuelve una promesa que se cumple si la operación es exitosa (el 70 % de las veces) y se rechaza si la operación falla. La función toma un arreglo de usuarios como argumento.
  • Simulación de retardo: para simular el tiempo que puede llevar almacenar datos en una base de datos, utilizamos setTimeout() para introducir un retraso de 2 segundos.
  • Uso de la función de agregación de usuarios: pasamos el arreglo nuevosUsuarios a la función agregarUsuarios(). Luego utilizamos .then() para manejar el caso de éxito, donde podríamos realizar acciones adicionales, y .catch() para manejar el caso de falla, donde podríamos mostrar un mensaje de error.
  • Datos de ejemplo de usuarios: creamos un arreglo de objetos que representan a los usuarios que deseamos agregar a la base de datos.

Como ves, las promesas en JavaScript son una herramienta esencial para manejar operaciones asíncronas de manera más legible, estructurada y eficiente. Proporcionan una forma de lidiar con el flujo de control en código asíncrono, permitiendo gestionar resultados exitosos y errores de una manera más ordenada. En resumen, han allanado el camino para un manejo más elegante y eficiente de operaciones asíncronas, lo que mejora la calidad del código y la experiencia del desarrollador en la creación de aplicaciones modernas y robustas.

Por último, recuerda que tienes más opciones para crear páginas web, mismas que combinen la flexibilidad y la sencillez.

New Call-to-action
 CMS Hub

Publicado originalmente el 11 de septiembre de 2023, actualizado el 12 de septiembre de 2023

Topics:

JavaScript