Las promesas se introdujeron en ECMAscript 6 y, aunque antes ya podríamos trabajar con ellas ya que existían librerías externas (de terceros) que las implementaban, no fue hasta esta versión en la que se hicieron forma nativa. Básicamente, nos ofrecen una forma más limpia de trabajar con tareas asíncronas.

¿Qué es una promesa?

Promesa = promise (en inglés)

Una promesa en JavaScript es similar a una promesa en la vida real, de ahí que reciban su nombre.

Cuando hacemos una promesa en la vida real (o en JavaScript), tratamos de afirmar que muestra intención de comprometernos a hacer algo en el futuro. Pero no todo lo que se promete se cumple, ya que podemos cruzar los dedos o faltar a nuestra palabra.

En esta especie de contrato verbal que acabamos de «firmar» comprometiéndonos a hacer algo en muchas ocasiones no se garantiza cuando pasará si será en un período breve o muy muy largo…. En las promesas nosotros seremos los encargados de definir el momento en el que evaluaremos si se ha cumplido o no.

Las promesas, por tanto, se enfocan en algo que pasará en el futuro.

Son la parte favorita de todo javaScript de los personjes Dog y Mike de regreso al futuro

Siendo más puristas, si hablamos de ellas en JS, una promesa es un objeto que representa la terminación o el fracaso de una operación asíncrona.

Creación de una promesa en JavaScript

Las promesas se crean llamando al constructor Promise y pasándole una función que recibe dos parámetros: resolve y reject, que nos permiten indicarle a esta que se resolvió o se rechazó.

Para ello, creamos un documento HTML en el que asociamos nuestro script (que contendrá la promesa):

Lo mejor de las promesas es que van dejando rastro. Aunque luego hablaremos de sus estados más profundamente las promesas cuando nacen tienen como estado siempre pendiente (peding) ya que aún no se han cumplido y, por tanto, están a la espera. En este caso en particular hemos dicho que prometemos algo, pero no hemos especificado ni el que.

Si ahora vamos al navegador y abrimos el inspector, vemos que nos aparece como peding:

Si directamente desde la propia consola del DevTool (el inspector del navegador) hacemos un typeof de nuestra promesa (miPrimeraPromesa) podemos ver que estamos trabajando con un objeto.

Promesas con Arrow function (=>)

Actualmente las promesas suelen trabajarse con arrow function (funciones de flecha) ya que son la manera moderna de trabajar. Hacen lo mismo con menos código y de una forma más visual. Vamos a ver un ejemplo:

Promesas como return dentro de una function

Si combinamos una función y una promesa con arrow function el resultado será:

Normalmente trabajaremos así con las promesas. Ya que así podremos almacenar las promesas dentro de una función y podremos pasarle parámetros y además, estarán mejor estructuradas.

Estados de las promesas

Como ya hemos visto un poco más arriba, las promesas tienen estados.

Esto nos lleva a hablar del estado de una promesa, básicamente existen 2 posibles estados.

  • Pendiente (pending): hemos hecho una promesa y estamos a la espera de ver si se cumple o no.
  • Resuelta (settled): la promesa se ha finalizado. El resultado de resolver una promesa (Settled) puede ser:
      • Fullfilled: se cumplió/resolvió con éxito.
      • Rejected: no se pudo cumplir a causa de un error. Depende de nosotros como hagamos el reject.

Una vez las promesas cambian su estado a resueltas (settled → a fulfilled o rejected), ya no pueden volver a cambiar de estado. Esto es debido a que ya hemos evaluado si la promesa de a cumplido o no.

Utilizar resolve y reject como nombre de los parametros de una promesa no es obligatorio, pero si una convención (una buena práctica) que nos permitirá identificarlos mejor ambos caminos.

De hecho, que se utilicen estos métodos surge debido a que si nos metemos dentro del constructor de la promesa podemos ver que los keys del objeto se llaman así:

Los best friends: Resolved y then & reject y catch

Hasta ahora hemos visto como declarar una promesa y cuáles son sus estados. En este punto vamos empezar a desarrollar el contenido del interior de nuestras promesas. Además, aprenderemos a evaluar si la promesa se ha cumplido o no en un determinado momento y ver cómo cambia el estado de la promesa de peding a fullfilled o rejected.

Ejercicio 1 ejemplo con una promesa declarada con function

Imaginaos que os prometo que si lanzamos una moneda al aire saldrá cara.

Aunque es una promesa con una probabilidad del 50 % existe otro 50 % de que no se cumpla.

Si miramos su estado ahora, podemos ver que tenemos fulfilled (resuelta).

Pero, cuando el resultado es cruz, podemos ver que nos aparece el error: Uncaught (in promise) Cruz quejándose de que no hemos atrapado el error:

Esto es debido a que para evaluar correctamente el resultado una promesa tenemos que utilizar los métodos .then y .catch sobre la promesa en sí. Vamos a ver un ejemplo:

.then → captura el valor cuando se cumple la promesa (fullfilled)

.catch → captura el valor cuando se produce un error o no se cumple la promesa (rejected)

El nombre de dentro del .catch y del .then no tiene por qué ser resolveMsg o err podemos ponerle el que querramos.

El resultado de evaluar una promesa no nos devolverá el objeto en si solamente el valor de resolve o reject. Si además hacemos un console.log del objeto recibiremos también el objeto completo y podremos ver su estado entre otras cosas.

Si se cumple lo que he prometido y sale cara la promesa aparece en fulfilled y el resultado que nos devuelve es cara.

Si se incumple lo que he prometido y sale cruz, ahora sí que podemos ver que la promesa aparece en rejected y el resultado que nos devuelve es cruz.

Como ya dijimos anteriormente, que prometas algo no significa que se vaya a cumplir…

Ejercicio 2 portero de discoteca con una promesa dentro de una function

Si hacemos un portero de discoteca que controle el acceso de las personas restringiendo a los menos de 18. Un ejemplo podría ser:

Si cambiamos el valor del parámetro con el que llamamos a la función que devuelve la promesa a 15, podemos ver que el portero nos dice:

Generando errores en promesas

Si evolucionamos nuestro código del portero de discoteca, podemos generar mensajes de errores. Por ejemplo ¿Qué pasaría si le pasamos un texto en vez de una edad? Pues hasta ahora que entraría en el catch y nos denegaría el acceso:

Una manera de solucionarlo es generarnos un mensaje de error. Vamos a ver un ejemplo:

El resultado esta vez será:

Promesas en cadena (promise chaining)

El promise chaining, nos permite encadenar varias promesas a la vez. Para ello, definimos una promesa y vamos concatenando las respuestas con .then

Realmente las promesas ofrecen una alternativa a tener en cuenta ya que pueden manejar múltiples operaciones asincrónicas fácilmente y brindan un mejor manejo de errores que las devoluciones de llamada y los eventos.

El trabajar con promesas y no con callbacks encadenados nos evitará el famoso callback hell o Pirámide de Doom.

El callback Hell o pirámide de doom, consiste en anidar múltiples Callbacks lo que provocan que el código se vuelva difícil de leer y de debuggear; ésta es la principal razón por la cual se debe evitar.

Promise chaining manera asíncrona, pero todas a la vez

Si el parametro de la función inputNumber es un tipo numérico, podemos ver que la pantalla se muestra en blanco:

Y tras dos segundos (el tiempo que hemos definido en la función asíncrona setTimeOut), nos devuelve los tres valores a la vez:

Si modificamos el parámetro de la funcion inputNumber que verifica la promesa, podemos ver que si por ejemplo le ponemos «a»:

Si nos fijamos de esta manera trabajamos con un solo setTimeOut por lo que inmediatamente han pasado los 3 segundos, se resuelven todas las promesas a la vez.

Promise chaining manera asíncrona, pero de una a una

En cambio, si queremos resolver las promesas una a una, de forma asíncrona, podemos hacer:

El resultado de cada una de las promesas irá mostrándose cada 3 segundos:

Tres segundos después:

Tres segundos despues:

Si nos fijamos de esta manera trabajamos con tres setTimeOut por lo que conforme van pasado los 3 segundos, se van resolviendo las promesas una a una.

Añadiendo un finally a nuestro promise chaining (de todas a la vez):

Finally, nos permite ejecutar un bloque al final de nuestro encadenamiento de promesas:

Como podemos ver el bloque finally se ejecuta siempre que la promesa sea evaluada y cambie de estado (settled) independiemente de que la promesa finalice en fulfilled o rejected

Ejercicio de ejemplo real de una promesa

Bueno, para acabar os dejo un ejercicio un poco más complejo.

Vamos a imaginar que dos hijos (Marta y Raúl) le dicen a su madre que les dé 30 €. De los que 10 € son para imprimir un trabajo del colegio y 20 € son para comer con unos amigos.

La madre le dice que no, que solamente les da 20 € porque le asusta que sus hijos tengan mucha hambre y les dé un impulso y se gasten los 30 € comiendo. Lo que supondría tender que volver a pagar 10 € más por cada uno de sus hijos, 20 € más en total y supondría no poder comprarse el bolso que ha visto en un escaparate esta mañana.

Pero sus hijos se ponen muy pesados y le juran y le perjuran que eso no pasará hasta el punto en el que se lo acaban prometiendo a su madre.

8 horas después, (en el ejemplo serán 8 segundos después) la madre vendrá de trabajar y evaluará si sus hijos han cumplido sus promesas. Y si no lo han hecho, echará bronca a quién la haya incumplido o a los dos en el caso de que los dos hayan incumplido a sus promesas. En el caso de que ambos cumplan, los felicitará.

Pero como ya dijimos anteriormente, que lo jures no significa que se vaya a cumplir…

Vamos a realizar este ejercicio con varias promesas que se concatenen entre si mediante al promise chaining que ya conocemos.

El ejemplo podría ser:

Si Marta incumple su promesa:

Tras 8 horas, podemos ver que al llevar su madre y que riñe a Marta:

Si se pasa Raúl, lo mismo, tras 8 horas, podemos ver que al llevar su madre y que riñe a Raúl:

Si incumplen su promesa los dos, se enfada aún más, y les dice:

Y, por último, si ambos cumplen su promesa, su madre se pone feliz y felicita a ambos:

Espero que os haya permitido familiarizarnos un poco más con el concepto de las promesas. Un saludo y hasta la próxima