This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Una función puede devolver un valor
function sumar(v1, v2) {
return v1 + v2
}
console.log(sumar(1, 2))
A veces obtener ese valor tarda tiempo. Por ejemplo si dependemos de una conexión a un servidor. Entonces en ese caso se proveía un callback para ser llamado cuando terminaba.
require('https').get('https://www.google.com.ar/', function(res) {
console.log(res.statusCode)
})
Esto funciona, pero tiene varios problemas. Por un lado, si queremos hacer más de una cosa cuando termina tenemos que hacer una función que conozca todo lo que hay que hacer. También si hay que hacer varias cosas en serie queda una como callback de otra como callback de otra y el código se hace difícil de leer.
Entonces ahí nace el concepto de Promesa. La Promesa representa una promesa de un valor futuro. Es decir que la operación no te devuelve el valor que querés sino un objeto que eventualmente tendrá ese valor.
Una implementación simplificada de Promise es algo así:
var https = {
get: function(url) {
var promise = {
resolved: false,
value: null,
callbacks: [],
resolve: function(value) {
this.value = value
this.resolved = true
for (var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i](value)
}
},
then: function(callback) {
if (this.resolved) {
callback(this.value)
} else {
this.callbacks.push(callback)
}
}
}
require('https').get(url, function(res) {
promise.resolve(res)
})
return promise
}
}
var promise = https.get('https://www.google.com.ar/')
promise.then(function(res) {
console.log(res.statusCode)
})
promise.then(function(res) {
console.log(res.headers['content-type'])
})
Promise fue agregado a Javascript por lo que no hay que hacer todo eso.
Una función puede crear una promesa. Al hacerlo recibe dos funciones, una para llamar cuando tiene
éxito y una para llamar cuando falla. Quien espera el valor puede agregar callbacks al éxito con
then
y al fallo con catch
const get = function(url) {
return new Promise((resolve, reject) => {
const lib = url.startsWith('https') ? require('https') : require('http');
const request = lib.get(url, (response) => {
if (response.statusCode < 200 || response.statusCode > 299) {
return reject(response);
}
resolve(response)
});
request.on('error', (err) => reject(err))
})
};
function run() {
get('https://www.google.com.ar/')
.then((res) => console.log(res.statusCode))
.catch((err) => console.error(err));
}
run()
Las promesas se pueden encadenar para que una se resuelva con el valor de una anterior.
const suma = function(a, b) {
return new Promise((resolve, reject) => { resolve(a+b) })
};
const resta = function(a, b) {
return new Promise((resolve, reject) => { resolve(a-b) })
};
function run() {
suma(1, 2)
.then((res) => {
console.log('el resultado de la suma es', res)
return resta(res, 5)
})
.then((res) => {
console.log('el resultado de la resta es', res)
})
}
run()
Esta sintaxis se puede hacer aún más linda con async y await. Esto fue agregado recientemente y puede no estar disponible en viejas plataformas.
const get = function(url) {
return new Promise((resolve, reject) => {
const lib = url.startsWith('https') ? require('https') : require('http');
const request = lib.get(url, (response) => {
if (response.statusCode < 200 || response.statusCode > 299) {
return reject(response);
}
resolve(response)
});
request.on('error', (err) => reject(err))
})
};
async function run() {
try {
const res = await get('https://www.google.com.ar/')
console.log(res.statusCode)
} catch (err) {
console.error(err)
}
}
run()
Este ejemplo hace exactamente lo mismo que el ejemplo anterior, pero usando la sintaxis nueva.
await
traduce automáticamente el código que continúa como si fuese código a ser ejecutado en
el then
de la Promise.
Sólo se puede llamar a await
dentro de una función marcada como async
.
La función al ser async
hace que su valor de retorno sea automáticamente una Promise, porque
si hubo una llamada a await
se necesita que ella hubiese concluido para tener su propio valor.
async function sumar(a, b) {
return a+b
}
sumar(1, 2).then((res) => console.log(res))
También se pueden combinar varias Promise en una sola para esperar a que todas terminen.
async function sumar(a, b) {
return a+b
}
async function restar(a, b) {
return a-b
}
async function esperar(time) {
return new Promise((resolve) => setTimeout(() => resolve(), time))
}
async function run() {
const [suma, resta, _] = await Promise.all([sumar(1, 2), restar(1, 2), esperar(1)])
console.log(suma, resta)
}
run()
Y encadenar cosas es más fácil con async/await!
const suma = async function(a, b) {
return a+b
};
const resta = async function(a, b) {
return a-b
};
async function run() {
const res_suma = await suma(1, 2)
console.log('el resultado de la suma es', res_suma)
const res_resta = await resta(res_suma, 5)
console.log('el resultado de la resta es', res_resta)
}
run()