javascript ecmascript-6 async-await

javascript - La función asincrónica no devuelve valor, pero console.log() sí: ¿cómo hacerlo?



ecmascript-6 async-await (3)

En cuanto a tu comentario; Lo agregaré como respuesta.

El código que escribe en JavaScript se ejecuta en un hilo, lo que significa que si su código realmente puede esperar algo, bloqueará la ejecución de cualquiera de sus otros códigos. El bucle de eventos de JavaScript se explica muy bien en este video y si desea leer en esta página .

Un buen ejemplo de código de bloqueo en el navegador es la alert("cannot do anything until you click ok"); . La alerta bloquea todo, el usuario ni siquiera puede desplazarse o hacer clic en nada en la página y su código también bloquea la ejecución.

Promise.resolve(22) .then(x=>alert("blocking")||"Hello World") .then( x=>console.log( "does not resolve untill you click ok on the alert:", x ) );

Ejecuta eso en una consola y verás a qué me refiero con bloqueo.

Esto crea un problema cuando quieres hacer algo que lleva tiempo. En otros marcos, usaría un hilo o procesos, pero no existe tal cosa en JavaScript (técnicamente existe con el trabajador web y la bifurcación en el nodo, pero esa es otra historia y, por lo general, mucho más complicada que usar la API asíncrona).

Entonces, cuando desee hacer una solicitud http, puede usar fetch pero fetch tarda un tiempo en finalizar y su función no debe bloquearse (tiene que devolver algo lo más rápido posible). Es por eso que fetch devuelve una promesa.

Tenga en cuenta que fetch es implementado por el navegador / nodo y se ejecuta en otro hilo, solo el código que escribe se ejecuta en un hilo, por lo que comenzar muchas promesas de que solo el código que escribe no acelerará nada más que llamar a la API asíncrona nativa en paralelo.

Antes de las promesas, el código asincrónico usaba devoluciones de llamada o devolvería un objeto observable (como XmlHttpRequest), pero cubramos las promesas ya que de todos modos puede convertir el código más tradicional en una promesa.

Una promesa es un objeto que tiene una función then (y un montón de cosas que es azúcar para entonces pero hace lo mismo), esta función toma 2 parámetros.

  1. Controlador de resolución: una función que la promesa invocará cuando la promesa se resuelva (no tiene errores y ha finalizado). A la función se le pasará un argumento con el valor de resolución (para solicitudes http, esta suele ser la respuesta).
  2. Controlador de rechazo: una función que la promesa invocará cuando la promesa rechace (tiene un error). A esta función se le pasará un argumento, este suele ser el error o el motivo del rechazo (puede ser una cadena, un número o cualquier cosa).

Convertir la devolución de llamada en promesa.

Las devoluciones de llamada de uso de la API tradicional (especialmente la API de nodejs):

traditionalApi( arg ,function callback(err,value){ err ? handleFail(err) : processValue(value); } );

Esto dificulta que el programador detecte errores o maneje el valor de retorno de forma lineal (de arriba a abajo). Se vuelve aún más imposible intentar hacer cosas paralelas o estranguladas en paralelo con el manejo de errores (imposible de leer).

Puedes convertir las API tradicionales en promesas con la new Promise

const apiAsPromise = arg => new Promise( (resolve,reject)=> traditionalApi( arg, (err,val) => (err) ? reject(err) : resolve(val) ) )

async espera

Esto es lo que se llama azúcar de sintaxis para promesas. Hace que las funciones prometedoras parezcan más tradicionales y fáciles de leer. Es decir, si le gusta escribir código tradicional, diría que componer pequeñas funciones es mucho más fácil de leer. Por ejemplo, ¿puedes adivinar qué hace esto ?:

const handleSearch = search => compose([ showLoading, makeSearchRequest, processRespose, hideLoading ])(search) .then( undefined,//don''t care about the resolve compose([ showError, hideLoading ]) );

Anayway; despotricar lo suficiente. La parte importante es entender que la async await realidad no inicia otro hilo, las funciones async siempre devuelven una promesa y la await realidad no bloquea ni espera. Es el azúcar de sintaxis para someFn().then(result=>...,error=>...) y se ve así:

async someMethod = () => //syntax sugar for: //return someFn().then(result=>...,error=>...) try{ const result = await someFn(); ... }catch(error){ ... } }

Los ejemplos siempre muestran try catch pero no necesita hacer eso, por ejemplo:

var alwaysReject = async () => { throw "Always returns rejected promise"; }; alwaysReject() .then( x=>console.log("never happens, doesn''t resolve") ,err=>console.warn("got rejected:",err) );

Cualquier error arrojado o en await devolver una promesa rechazada hará que la función asíncrona devuelva una promesa rechazada (a menos que intente atraparla). Muchas veces es deseable simplemente dejar que falle y que la persona que llama maneje los errores.

Los errores de captura podrían ser necesarios cuando desea que la promesa tenga éxito con un valor especial para las promesas rechazadas para que pueda manejarla más tarde, pero la promesa no se rechaza técnicamente, por lo que siempre se resolverá.

Un ejemplo es Promise.all , esto toma una serie de promesas y devuelve una nueva promesa que se resuelve en una matriz de valores resueltos o rechaza cuando cualquiera de ellos rechaza . Es posible que solo desee recuperar los resultados de todas las promesas y filtrar las rechazadas:

const Fail = function(details){this.details=details;}, isFail = item => (item && item.constructor)===Fail; Promise.all( urls.map(//map array of urls to array of promises that don''t reject url => fetch(url) .then( undefined,//do not handle resolve yet //when you handle the reject this ".then" will return // a promise that RESOLVES to the value returned below (new Fail([url,err])) err=>new Fail([url,err]) ) ) ) .then( responses => { console.log("failed requests:"); console.log( responses.filter(//only Fail type isFail ) ); console.log("resolved requests:"); console.log( responses.filter(//anything not Fail type response=>!isFail(response) ) ); } );

Esta pregunta ya tiene una respuesta aquí:

Tengo una clase es6, con un método init() responsable de recuperar datos, transformarlos y luego actualizar la propiedad de la clase this.data con datos recién transformados. Hasta ahora tan bueno. La clase en sí tiene otro método getPostById() , para hacer lo que parece. Aquí está el código para la clase:

class Posts { constructor(url) { this.ready = false this.data = {} this.url = url } async init() { try { let res = await fetch( this.url ) if (res.ok) { let data = await res.json() // Do bunch of transformation stuff here this.data = data this.ready = true return data } } catch (e) { console.log(e) } } getPostById(id){ return this.data.find( p => p.id === id ) } }

Directo, excepto que tengo un mecanismo async/await en el método init() . Ahora, este código funcionará correctamente:

let allPosts = new Posts(''https://jsonplaceholder.typicode.com/posts'') allPosts.init() .then( d => console.log(allPosts.getPostById(4)) ) // resulting Object correctly logged in console

pero solo se imprime en la consola: ¿Cómo podría usar allPosts.getPostById(4) como return de una función?

Me gusta:

let myFunc = async () => { const postId = 4 await allPosts.init() // I need to wait for this to finish before returning // This is logging correct value console.log( ''logging: '' + JSON.stringify(allPosts.getPostById( postId ), null, 4) ) // How can I return the RESULT of allPosts.getPostById( postId ) ??? return allPosts.getPostById( postId ) }

myFunc() devuelve una Promise pero no el valor final. He leído varias publicaciones relacionadas sobre el tema, pero todas dan ejemplos de registro, nunca regresan.

Aquí hay un violín que incluye dos formas de manejar init() : usando Promise y usando async/await . No importa lo que intente, no puedo getPostById(id) VALOR FINAL de getPostById(id) .

La pregunta de esta publicación es: ¿cómo puedo crear una función que DEVUELVA el VALOR de getPostById(id) ?

EDITAR:

Muchas buenas respuestas tratando de explicar cuáles son las promesas con respecto al ciclo de ejecución principal. Después de muchos videos y otras buenas lecturas, esto es lo que entiendo ahora:

mi función init() regresa correctamente. Sin embargo, dentro del bucle principal del evento: devuelve una Promesa , entonces es mi trabajo atrapar el resultado de esta Promesa desde un bucle paralelo (no un nuevo hilo real). Para captar el resultado del bucle paralelo, hay dos formas:

  1. use .then( value => doSomethingWithMy(value) )

  2. use let value = await myAsyncFn() . Ahora aquí está el hipo tonto:

await solo se puede usar dentro de una función async : p

por lo tanto, se devuelve una Promesa, utilizable con await que debe integrarse en una función async , que se puede utilizar con await etc.

Esto significa que realmente no podemos ESPERAR una promesa: en su lugar, deberíamos atrapar un bucle paralelo indefinidamente: usando .then() o async/await .

Gracias por la ayuda !


Su pregunta y los comentarios sugieren que podría usar un poco de intuición sobre cómo funciona el ciclo de eventos. Al principio es realmente confuso, pero después de un tiempo se convierte en una segunda naturaleza.

En lugar de pensar en el VALOR FINAL, piense en el hecho de que tiene un solo hilo y no puede detenerlo, por lo que desea el VALOR FUTURO, el valor en el próximo o algún bucle de evento futuro. Todo lo que escriba que no sea asíncrono va a suceder casi de inmediato: las funciones regresan con algún valor o no están definidas de inmediato . No hay nada que puedas hacer al respecto. Cuando necesita algo de forma asíncrona, debe configurar un sistema que esté listo para manejar los valores asíncronos cuando regresen en algún momento en el futuro. Esto es con lo que todos los eventos, devoluciones de llamada, promesas (y async / wait) intentan ayudar. Si algunos datos son asíncronos, simplemente no puede usarlos en el mismo bucle de eventos.

Entonces, ¿Qué haces?

Si desea un patrón donde cree una instancia, llame a init() y luego a alguna función que lo procese aún más, simplemente necesita configurar un sistema que procese cuando lleguen los datos. Hay muchas formas de hacer esto. Aquí hay una forma que es una variación en su clase:

function someAsync() { console.log("someAsync called") return new Promise(resolve => { setTimeout(() => resolve(Math.random()), 1000) }) } class Posts { constructor(url) { this.ready = false this.data = "uninitilized" this.url = url } init() { this.data = someAsync() } time100() { // it''s important to return the promise here return this.data.then(d => d * 100) } } let p = new Posts() p.init() processData(p) // called twice to illustrate point processData(p) async function processData(posts) { let p = await posts.time100() console.log("randomin * 100:", p) }

init() guarda la promesa devuelta por someAsync() . someAsync() podría ser cualquier cosa que devuelva una promesa. Guarda la promesa en una propiedad de instancia. Ahora puede llamar a then() o usar async / await para obtener el valor. Devolverá inmediatamente el valor si la promesa ya se ha resuelto o lo tratará cuando se haya resuelto. Llamé a processData(p) dos veces solo para ilustrar que no someAsync() dos veces.

Ese es solo un patrón. Hay muchos más: usar eventos, observables, simplemente usando then() directamente, o incluso devoluciones de llamada que no están de moda, pero que aún pueden ser útiles.


NOTA: Donde sea que use await , debe estar dentro de una función async .

Mira el FIDDLE ACTUALIZADO

await myFunc() usar await myFunc() para obtener el valor que espera de getPostById porque una función asincrónica siempre devuelve una promesa.

Esto a veces es muy frustrante ya que toda la cadena necesita convertirse en funciones async , pero supongo que ese es el precio que paga por convertirlo a un código síncrono. No estoy seguro de si eso se puede evitar, pero estoy interesado en escuchar a las personas que tienen más experiencia en esto.

Pruebe el siguiente código en su consola copiando las funciones y luego acceda a final y await final .

NOTA:

Una función asíncrona PUEDE contener una expresión de espera. MDN

No hay una regla que deba haber esperado para declarar una función asincrónica. El siguiente ejemplo utiliza una función asincrónica sin esperar solo para mostrar que una función asincrónica siempre devuelve una promesa.

const sample = async () => { return 100; } // sample() WILL RETURN A PROMISE AND NOT 100 // await sample() WILL RETURN 100 const init = async (num) => { return new Promise((resolve, reject) => { resolve(num); }); } const myFunc = async (num) => { const k = await init(num); return k; } // const final = myFunc(); // final; This returns a promise // await final; This returns the number you provided to myFunc