Bien, en este post vamos a ver algunos extras que no son exclusivos de Node pero realmente son muy útiles.
Promesas
Las promesas, si no estoy mal, se añadieron oficialmente en JS a partir de ES6...y por resumir un poco, son como callbacks de finalización pero en bonito, explicome....cuando necesitamos encadenar métodos que continenen callbacks podemos terminar con un código de este estilo
Realmente es raro que nuestro código termine así, pero realmente esa es la tendencia , ¿verdad?.. bueno pues una de las cosas que nos ayudan a mejorar las promesas es esa. Por definir concretamente:
Una promesa es un objeto que, si todavía no ha terminado, aseguramos que se completará más adelante.
Supongo que con esa frase queda más claro todavía qué es una promesa (y también su nombre jeje). Las promesas tienen tres estados en su ciclo de vida:
- Pendiente(pending): Como su nombre indica todavía está sin cumplir y cambiará de estado en algún momento.
- Completada correctamente(Fullfilled/resolve): Es decir, ha hecho su trabajo correctamente y ha devuelto un valor.
- Rechazada(rejected): En su proceso de ejecución ha dado algún tipo de error y ha sido completada con error devolviendonos la razón.
Veamos ahora como podemos crear una promesa
var prom = new Promise(function(resolve,reject){
})
Básicamente creamos un nuevo objeto Promise el cual tiene una función que recibe dos parámetros, con un nombre bastante intuitivo por cierto:
- resolve: Llamaremos a resolve cuando todo haya ido bien y le pasamos el resultado que recibirá la siguiente promesa o función.
- reject: Llamaremos a reject cuando tengamos un error, al cual le pasaremos el error.
Y, ¿como usamos esto? Bastante sencillo
prom.then(function(resultado){
//Lo que queramos hacer con el resultado
}).catch(function(error){
//Lo que queramos hacer si hemos recibido error
});
Esto lo que hace es ejecutar la promesa que hemos creado y a continuación (then) se ejecutará la función dentro del then donde resultado es el parámetro que hemos recibido desde la promesa con resolve. En el caso de que algo hubiera fallado o el resultado fuera insatisfactorio ejecutariamos reject con el error o la información que quisieramos, la cual capturaría el primer catch que encontrará la cadena de promesas. Por si no os lo habéis imaginado nosotros podemos poner todas las promesas y catch que queramos
prom.then().then().then().catch().then().catch()
Y en el orden que las hemos escritor se irian ejecutando, y también según ese orden el primer catch que se encontrara sería el que respondería en caso de tener un resultado reject.
Hagamos un ejemplo encadenando promesas y de paso le ponemos algún catch. Vamos a crear un método nuevo junto con el que ya teníamos que lo vamos a modificar, la idea es que cada promesa vaya encadenando un String hasta mostrarlo al final, aunque en este caso lo que haremos será mostrarlo en cada paso para que se vea que esta haciendo.
Primero cambiamos nuestra función sleep
//Seria una funcion que devuelve una promesa
function sleep(ms,hero) {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve("La liga de la justicia: " + hero)
}, ms);
});
}
const prom = sleep(2000, "Superman");
Le pasamos el parámetro hero y devolvemos un string. A continuación creamos otro método que vaya encadenando
function heros(result, hero){
return new Promise((resolve,reject) => {
/*if (hero === "Green Latern"){
reject("No aparece esta vez")
}*/
console.log(result)
setTimeout(()=> {
resolve(result + " " + hero)
},2000)
})
}
De momento dejamos comentada la parte de reject para ver el resultado. Como véis la función recibe un parámetro result (este primer parámetro sería lo que nos devuelve la promesa en caso de resolve) y también un parámetro extra con el nombre de otro hero y luego simplemente lo juntamos todo para que lo devuelva en 2 segundos.
Ya tenemos nuestros métodos ahora vamos a encadenar las promesas
prom
.then((result) => {
return heros(result, "Batman")
})
.then((result) => {
return heros(result, "Green Latern")
})
.then((result) => {
return heros(result, "Wonder Woman")
})
.catch((err) => {
console.log(err)
})
.then((result) => {
return heros(result, "Aquaman")
})
.then((result)=> {
console.log(result)
})
De paso le he metido un catch para probar el error más adelante. Fijáos que lo que hacemos es retornar dentro de una función anónima, que es la que recibe result, la promesa resultante de nuestra función heros, si no necesitaramos result no tendríamos porque meterlo dentro de otra función, bastaría solo con usar heros("New Hero"). Bueno y ahora si lo probamos
Vemos como cada 2 segundos nos aparece el string con el nuevo héroe.
Por último vamos a probar el catch para que veais como funciona, lo que haremos será descomentar el código que tenía reject y ya podríamos probarlo.
Vemos que cuando le toca el turno a Green Latern pasa por el catch pero no se detiene y continua......eso es porque tenemos que cambiar como funciona el catch ya que solo hemos puesto un console.log para que se vea que pasa por allí, para hacer que se detenga podemos poner
catch((err) => {
throw err
})
Y ahora sí tendríamos un error al estilo promesas
Con promesas tenemos también otras opciones, por ejemplo podemos pasar un array de promesas y cuando terminen todas pasa al then o al catch según si ha dado error o no.
Promise.all(arrayPromises)
.then((result) =>{console.log(result)})
.catch((error){})
Con Promise.all, pasamos una array de promesas y esperamos a que terminen todas, tenemos también la opción de que pase al then cuando responda la primera
Promise.race([arrayPromises])
.then((result) => { console.log("La más rápida ha sido" + result)})
Este concepto es útil por ejemplo si tenemos varios servidores de base de datos, hacemos las llamadas pertinentes en cada una de las promesas y en cuanto uno termine ya tenemos un resultado, lo que nos permite responder lo más rápido posible ignorando si un servidor tiene más carga que otro.
Ahora pasemos al siguiente nivel en las promesas, no en dificultad, pero si en un extra o forma de usarlas que, si no me equivoco, salió en ES7 y para esto es necesario tener Node actualizado a la última versión o por lo menos en la 7.6 (creo), con node -v podéis ver en que versión estáis y actualizar si lo necesitáis.
Concretamente vamos a hablar de async/await. Estos nuevos métodos nos ayudan un poco con la gestión de las promesas, y de los callbacks por supuesto. Lo que hacemos con await es esperar a que una función termine y parece ser que como best practice(o que tiene que ser si o sí más bien) tenemos que hacer ese código asíncrono asi que nos han puesto async para facilitarnos esa tarea. Y como funciona esto? Pues vamos a ver un ejemplo sencillo con lo que más o menos teníamos antes.
Partimos de esto
'use strict'
//Seria una funcion que devuelve una promesa
function sleep(ms,hero) {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve("La liga de la justicia: " + hero)
}, ms);
});
}
Se supone que tenemos que usar await para que el código se espere a que termine la promesa y meterlo todo dentro de async...pues vamos
async function asyncPromise(){
console.log("Empezamos a contar")
return await sleep(2000,"Superman")
}
Como véis simplemente hemos puesto await al principio de la ejecución de nuestra promesa, la hemos retornado porque si no, no obtendríamos resultado y lo hemos metido todo dentro de una función que empieza con async. Ahora vamos a usarlo como si de una promesa normal se tratase
asyncPromise().then((result) => console.log(result))
Bien ya tenemos todo, ahora vamos a ejecutarlo
Como habréis podido comprobar nos ha aparecido el log indicando que empieza y a los 2 segundos nos ha mostrado el resultado como si de una promesa normal se tratase. Este tipo de estructura realmente nos puede ahorrar el uso de callbacks de finalización o similar. Esto es solo un ejemplo de lo que se puede hacer con estas opciones "nuevas" de node, pero se puede usar de otras muchas maneras, el límite lo ponéis vosotros de como usar esto.
Creo que por el momento vamos a dejar las promesas (o lo relacionado con ellas) aquí, en algún otro momento volveremos a trastear con ellas pero creo que hemos visto más o menos como se tienen que usar y en que nos pueden ayudar. Creo que solo me quedaría un mini-post más de extras de Node y nos pondríamos ya con MongoDB.
Nos veeeemosssss un abrazooorrrrr





