3

Creo que tengo una falla en mi entendimiento de cómo funcionan las promesas:

Me interesa hacer esto:

resultado = asincrona()

async function asincrona() {
    promesa
    funciona:    return datos
    no funciona: return error
}

He probado todas las sintaxis dentro de mi función asíncrona y el resultado siempre es el mismo:

  1. Hace la promesa.
  2. Sale de la función(Con resultado undefined).
  3. Ejecuta el then o el catch.

Todos los ejemplos que he visto usan console.log para mostrar una u otra instancia, pero nunca hacen algo con el resultado.

Mi pregunta es: ¿Cómo obtengo el resultado de mi promesa?

¿Voy a tener que suplantar resultado = asincrona() con la evaluación de la promesa?

Mauricio Contreras
  • 13,660
  • 3
  • 18
  • 40
Rafa Gomez
  • 81
  • 2
  • 2
    Bienvenido a StackOverflow. Te invito a hacer el recorrido, para aprender sobre el funcionamiento del sitio y de paso ganar tu primera medalla. También sugiero que incluyas en tu pregunta un [mcve], ya que sin ver tu código se hace difícil entender cuál es el problema que estás teniendo. – jachguate Sep 13 '19 at 18:50
  • Hola, tal vez te sirva revisar [¿Qué es una promesa en Javascript?](https://es.stackoverflow.com/q/64265/77879) – the-breaker Sep 13 '19 at 19:53
  • Creo que deberías escribir no pseudo-código sino la verdadera implementación tal cual en JS, para que podamos ayudar con mayor precisión. Por ejemplo las promesas deben ser ejecutadas con `await asincrona()`. – Juan Solano Sep 13 '19 at 20:25

2 Answers2

4

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:

  1. Estás creando una Promesa dentro de tu función asíncrona usando new Promise.
  2. 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:

  1. Toda función tipo async devuelve una Promesa
  2. Toda Promesa tiene un método then() que podemos usar para trabajar con el resultado de la Promesa.
  3. 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.

Mauricio Contreras
  • 13,660
  • 3
  • 18
  • 40
1

Una de las manera mas sencillas de manejar una promesa es con una función que retorna un (new Promise): Ejemplo de una promesa (Que comprueba números iguales)

// Creamos nuestra funcion que retorna una promesa
function SonIgualesPromesa(numero1,numero2) {
    // resuelto: Le pasamos la respuesta cuando todo esta bien
    // rechazado: pasamos la respuesta cuando esta mal
    return new Promise(function (resuelto,rechazado) {
        if (numero1 == numero2) {
            resuelto('Son Iguales');
            return;
        }
        if (numero1 != numero2) {
            rechazado('Son Diferentes')
        }
    })
}  

Ahora para llamarlo y obtener su resultado solo tenemos que colocar:

// Llamamos nuestra promesa
SonIgualesPromesa(2,2).then(respuestaOk =>{
    // then: Esto se ejecutara si esta bien
    console.log('Tu promesa responde: ' +respuestaOk)
}).catch(respuestaMal =>{
    // catch: Esto se ejecutara si esta mal
    console.log('Tu promesa responde: ' +respuestaMal)
})  

De esta manera evitamos muchos errores y el código es fácil de entender

Andrus Diaz
  • 180
  • 10
  • Después de más de un año leyendo material acerca del await, entiendo por fin el flujo de la ejecución. ¡MUCHAS GRACIAS! – Rafa Gomez Sep 16 '19 at 17:01
  • Después de más de un año leyendo material acerca del await, entiendo por fin el flujo de la ejecución. – Rafa Gomez Sep 16 '19 at 17:01