tl;dr
RESUMEN
Para utilizar los datos devueltos por una Promesa puedes hacer uso de:
- Método
then()
, pasando una función como argumento a este método que se ejecutará una vez que la Promesa sea resuelta.
- Envolver la Promesa en una función tipo
async
y usar la palabra clave await
, para que las instrucciones subsiguientes (dentro de la función async
) sean ejecutadas una vez que la Promesa sea resuelta.
Respuesta larga
Ciertamente tienes una confusión en lo que es una Promesa y lo que es una Función Asíncrona y cómo relacionar ambas.
En tu código tienes:
resultado = asincrona()
Estás asignando a tu variable resultado
el valor devuelto por una función llamada asincrona()
. La cual más adelante defines en tu código como una función tipo async
. Pero luego, dentro de dicha función tienes una Promesa.
Dado que no proporcionas un ejemplo claro de lo que hay en tu función, asumiré sólo los siguientes 2 escenarios:
- Estás creando una Promesa dentro de tu función asíncrona usando
new Promise
.
- Estás haciendo una llamada a un método o función que devuelve una Promesa.
En ambos casos no necesitas declarar una Función Asíncrona si lo que vas a devolver es una Promesa.
Es un poco confuso al principio, ya que tendemos a pensar que Promesa implica Función Asíncrona, sin embargo esto no es cierto. (Me refiero a función tipo async
).
Lo que sí es cierto es que Función Asíncrona implica Promesa. Toda función asíncrona devuelve una Promesa.
Veamos:
async function miFuncion() {
return 'Soy una Promesa';
}
miFuncion().then(console.log);
Por lo tanto lo que almacena tu variable resultado
es una Promesa, ya que estás asignando la ejecución de una función asíncrona a la misma.
Si modificamos un poco el código anterior podemos observar lo siguiente:
async function asincrona() {
return 'Este es el resultado';
}
const resultado = asincrona();
console.log('¿Es resultado una Promesa? ', resultado instanceof Promise);
resultado.then(console.log);
Vamos a demostrar que no necesitas usar una Función Asíncrona si la función devuelve una Promesa.
Por ejemplo:
function miFuncion() {
return Promise.resolve('Soy una promesa');
}
const resultado = miFuncion();
console.log('¿Es resultado una Promesa? ', resultado instanceof Promise);
resultado.then(console.log);
Vemos claramente que se devuelve también una promesa, aunque nuestra función no ha sido declarada con async
.
Una promesa siempre implementa el método then()
, que ya has visto que he usado en los ejemplos anteriores. Es aquí donde que puedes usar el resultado de tu promesa.
Debemos entender que este método no es un llamado a ejecución inmediata. El método then()
recibe por lo general una función que se ejecutará cuando la Promesa se resuelva; y eso puede ocurrir ahora, en un futuro próximo o nunca.
Por ejemplo:
function miFuncion() {
return Promise.resolve('Soy una promesa.');
}
function miFuncion2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Soy una promesa de 2 segundos');
}, 2000);
});
}
const resultado = miFuncion();
const resultado2 = miFuncion2();
resultado.then(console.log); // aquí no se ejecuta la llamada a console.log
resultado2.then(console.log);// solo estoy indicando qué hacer cuando se resuelva la promesa.
console.log('Mientras esperamos...');
console.log('1.- Podemos llamar a otros procesos:');
const fecha = new Date(Date.now());
console.log('2.- La fecha de hoy es: ', fecha.toUTCString());
console.log('3.- ... y al finalizar:');
Ya tenemos un poco más claro el concepto de Promesa y también aprendimos que una Función Asíncrona devuelve una Promesa.
Volviendo a tu pregunta:
¿Cómo obtengo el resultado de mi promesa?
Pues si como resultado te refieres a los datos procesados por nuestra Promesa, ya hemos visto que usando then()
podemos obtenerlos.
Sin embargo, imagino que tu duda viene al querer usar dicho resultado en el ámbito Global o en algún ámbito fuera de la función pasada a then()
.
Vamos a ver un ejemplo de cómo podríamos usarla fuera del ámbito (no Global) de la función pasada a then()
.
Supongamos que tengo un método que obtiene un dato de forma asíncrona mediante una Promesa. Deseamos usar el valor devuelto por nuestra Promesa dentro del método. ¿Cómo logramos esto?
Aquí es donde entra en juego nuestra función tipo async
y la palabra reservada await
.
Según la documentación, await
le indica a Javascript que espere el resultado de la Promesa, pausando la ejecución del resto de instrucciones que siguen después de la llamada a await
.
async function miFuncion() {
const resultado = await Promise.resolve('No soy una promesa');
console.log('¿resultado es una Promesa? ', resultado instanceof Promise);
console.log(resultado);
}
miFuncion();
¿Qué cambió aquí?
El código es prácticamente el mismo que hemos venido trabajando, pero ahora nuestra Promesa es llamada usando la palabra await
, por lo tanto ahora resultado
ya no es una Promesa, sino que tiene el valor devuelto por la misma. Además, el código se ejecuta en orden, como es de esperar en un proceso no asíncrono.
Si esto funciona nos podemos ver tentados a realizar lo siguiente:
async function miFuncion() {
const resultado = await Promise.resolve('No soy una promesa');
// como resultado tiene un valor que no es Promesa
// intento mandarlo como return de mi función
return resultado;
}
const resultado2 = miFuncion();
console.log(resultado2);
Qué decepción.
Si hemos pensado que podíamos hacer eso, es que debemos repasar lo que acabo de decir sobre las funciones tipo async
. Todas las funciones tipo async
devuelven una Promesa.
Por lo tanto resultado2
no será el valor de resultado
sino que es una Promesa.
Sin embargo, hemos aprendido que podemos obtener el resultado de una Promesa si usamos await
. Pero await
sólo puede ser usado dentro de funciones tipo async
, y dichas funciones devuelven una Promesa.
Estamos de nuevo donde empezamos.
Ámbito Global
Lo cierto es que tal vez necesitemos usar el valor devuelto por nuestra Promesa en un ámbito superior (pongamos el ámbito Global).
Aunque existe la propuesta para permitir que await
se use fuera de funciones tipo async
, esto aún no ha sido implementado de forma estándar (tampoco sabría decir si al final se implementará).
Para ES7 hay una propuesta de un llamado usando do
:
let result = do async {
await something;
}
Mientras tanto, podemos usar un pequeño atajo o truco (nada aquí es magia).
Y es que podemos envolver todo nuestro código en una función tipo async
autoejecutada (autollamada). De esta forma podemos hacer cosas que parecen muy sincronizadas de forma asíncrona.
Ejemplo:
(async function miGlobal() {
function miFuncion() {
return Promise.resolve('Soy una promesa');
}
function miFuncion2() {
return Promise.resolve('Yo también soy una promesa');
}
function miFuncion3() {
return Promise.reject('Yo rompí la promesa');
}
const resultado = await miFuncion();
console.log(resultado);
const resultado2 = await miFuncion2();
console.log(resultado2);
try {
const resultado3 = await miFuncion3();
console.log(resultado3);
}
catch(e) {
console.log('Upss, algo salió mal');
console.log(e);
}
})();
Como puedes observar he utilizado una función autollamada que envuelve a mi función tipo async
:
(async function functionName() {
// aqui va tu código
})(); // <- aqui estoy indicando la llamada
Puedes leer esta respuesta en Github que aclara un poco más esto que acabo de explicar.
Conclusión
Podemos resumir todo esto de la siguiente forma:
- Toda función tipo
async
devuelve una Promesa
- Toda Promesa tiene un método
then()
que podemos usar para trabajar con el resultado de la Promesa.
- Si deseamos usar el resultado de la Promesa fuera del ámbito de
then()
, debemos envolver la Promesa en una función tipo async
y utilizar la palabra await
para esperar por el resultado de la Promesa.
Nota
Una buena práctica (que he omitido por cuestiones de simplicidad en el código) es usar bloques try catch
cuando hacemos llamados a métodos asíncronos que devuelven una Promesa usando await
.
En el último ejemplo puedes notar que usé un bloque try catch
para el llamado a miFuncion3
.
El mensaje que se muestra 'Upss, algo salió mal'
está dentro de la sección catch
.
Y es que cuando una Promesa es rechazada, la misma lanza una excepción, para indicar que algo salió mal.
Es por ello que siempre que trabajamos con Promesas y await
debemos usar bloques try catch
.
Espero que esto aclare tus dudas con respecto a las Promesas.