Después de tanto luchar ya hallamos la solución de esta duda que muchos usuarios de este sitio presentan. Si quieres compartir algo puedes aportar tu conocimiento.
Solución:
Todos async
las funciones devuelven una promesa. Todos ellos.
Esa promesa eventualmente se resolverá con cualquier valor que devuelva de la función asíncrona.
await
solo bloquea la ejecución interna al async
función. No bloquea nada fuera de la función. Conceptualmente, una función asincrónica comienza a ejecutarse y tan pronto como llega a un await
instrucción, devuelve inmediatamente una promesa incumplida de la función y el mundo de ejecución exterior obtiene esa promesa y continúa ejecutándose.
Algún tiempo después, la promesa interna que estaba siendo await
ed se resolverá y luego continuará la ejecución del resto de los componentes internos de la función. Eventualmente, los componentes internos de la función terminarán y devolverán un valor. Eso activará la resolución de la promesa que se devolvió de la función con ese valor de retorno.
Para su información, hay muchas cosas superfluas en su load()
función. Puedes cambiarlo de esto:
async function load()
const data = await new Promise(resolve =>
setTimeout(() => resolve([1, 2, 3]), 10);
).then(data => data.map(i => i * 10));
console.log(`Data inside the function: $JSON.stringify(data)`);
return data;
a esto:
function load()
return new Promise(resolve =>
setTimeout(() => resolve([1, 2, 3]), 10);
).then(data => data.map(i => i * 10));
Luego, utilícelo así:
load().then(result =>
console.log(result);
);
O prefiero encapsular la creación manual de la promesa en su propia función de esta manera:
function delay(t, v)
return new Promise(resolve =>
setTimeout(resolve.bind(null, v), t);
);
function load()
return delay(10, [1, 2, 3]).then(data => data.map(i => i * 10));
Y resulta que este pequeño delay()
La función es generalmente útil en muchos lugares donde desea retrasar una cadena de promesas.
Gracias a todos los que participaron y me brindaron información. Pero todavía estoy confundido sobre cómo debería usar await y async.
En primer lugar, la mayoría de las veces solo marca una función async
si necesitas usar await
dentro de la función.
En segundo lugar, usas más comúnmente await
(desde dentro de un async
función) cuando tiene varias operaciones asincrónicas y desea secuenciarlas, a menudo porque la primera proporciona un resultado que se utiliza como entrada para la segunda. Puedes usar await
cuando todo lo que tiene es una sola operación asincrónica, pero realmente no ofrece mucha ventaja sobre una simple .then()
.
Aquí hay algunos ejemplos de buenas razones para usar async/await
:
Secuencia de múltiples operaciones asincrónicas
Imagina que tienes getFromDatabase()
, getTheUrl()
y getTheContent()
que son todos asincrónicos. Si alguno falla, querrá simplemente rechazar la promesa devuelta con el primer error.
Así es como se ve esto sin async / await:
function run()
return getFromDatabase(someArg).then(key =>
return getTheURL(key);
).then(url =>
return getTheContent(url);
).then(content =>
// some final processing
return finalValue;
);
Así es como se ve esto con async/await
:
async function run(someArg)
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
En ambos casos, la función devuelve una promesa que se resuelve con el valor final, por lo que estas dos implementaciones son utilizadas por la persona que llama de la misma manera:
run(someArg).then(finalValue =>
console.log(finalValue);
).catch(err =>
console.log(err);
);
Pero, notará que el async/await
La implementación tiene un aspecto más serializado y sincrónico y se parece más a un código no asincrónico. Muchos encuentran esto más fácil de escribir, más fácil de leer y más fácil de mantener. Cuanto más procesamiento tenga entre los pasos, incluida la ramificación, más ventajas obtendrá el async/await
versión.
Detectar automáticamente tanto las promesas rechazadas como las excepciones sincrónicas
Como dije anteriormente, async
las funciones siempre devuelven una promesa. También tienen un manejo de errores incorporado que propaga automáticamente los errores a esa promesa devuelta.
No hace falta decir que si devuelve manualmente una promesa del async
función y esa promesa se rechaza, entonces la promesa regresó de la async
la función rechazará.
Pero también, si está utilizando await
y cualquier promesa que está esperando se rechaza y no tiene un .catch()
en la promesa y no tengo un try/catch
a su alrededor, la promesa que devuelve la función se rechazará automáticamente. Entonces, de vuelta en nuestro ejemplo anterior de esto:
async function run(someArg)
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
Si alguna de las tres promesas que se están haciendo await
ed rechazar, entonces la función cortocircuitará (dejará de ejecutar más código en la función) y rechazará la promesa devuelta asíncrona. Entonces, obtienes esta forma de manejo de errores de forma gratuita.
Luego, por último, un async
La función también detecta las excepciones sincrónicas y las convierte en una promesa rechazada.
En una función normal que devuelve una promesa como la que teníamos anteriormente:
function run()
return getFromDatabase(someArg).then(key =>
return getTheURL(key);
).then(url =>
return getTheContent(url);
).then(content =>
// some final processing
return finalValue;
);
Si getFromDatabase()
lanza una excepción sincrónica (quizás activada porque someArg
no es válido), entonces esta función general run()
lanzará sincrónicamente. Eso significa que para que la persona que llama capte todos los posibles errores de run()
, ambos tienen que rodearlo con un try/catch
para detectar las excepciones sincrónicas y utilizar un .catch()
para atrapar la promesa rechazada:
try
run(someArg).then(finalValue =>
console.log(finalValue);
).catch(err =>
console.log(err);
);
catch(e)
console.log(err);
Esto es complicado y un poco repetitivo. Pero cuando run()
se declara async
, entonces NUNCA se lanzará sincrónicamente porque cualquier excepción sincrónica se convierte automáticamente en una promesa rechazada, por lo que puede estar seguro de que está capturando todos los errores posibles cuando se escribe de esta manera:
async function run(someArg)
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
// will catch all possible errors from run()
run(someArg).then(finalValue =>
console.log(finalValue);
).catch(err =>
console.log(err);
);
¿Debo llamar siempre a todas mis funciones con una espera?
Primero, solo usarías await
con una función que devuelve una promesa como await
no ofrece ninguna utilidad si la función no devuelve una promesa (solo se agrega al desorden de su código si no es necesario).
En segundo lugar, si usa await
o no depende del contexto de la función de llamada (ya que TIENE que estar en un async
función a utilizar await
y sobre el flujo de la lógica y si se beneficia del uso await
o no.
Lugares donde no tiene sentido usar aguardan
async function getKey(someArg)
let key = await getFromDatabase(someArg);
return key;
los await
aquí no está haciendo nada útil. No está secuenciando varias operaciones asíncronas y no está procesando el valor de retorno. Puede lograr exactamente el mismo código simplemente devolviendo la promesa directamente:
async function getKey(someArg)
return getFromDatabase(someArg);
Y si sabes eso getFromDatabase()
nunca lanza sincrónicamente, incluso puede quitar el async
de la declaración:
function getKey(someArg)
return getFromDatabase(someArg);
Digamos que estoy escribiendo un código compuesto por múltiples funciones dentro de múltiples archivos. Si termino usando una biblioteca que devuelve una Promesa o es una función asíncrona, ¿debo rastrear todas mis llamadas de función desde el punto asincrónico hasta el punto de entrada de la aplicación y agregar una espera antes de todas las llamadas de función después de hacerlas asíncronas?
Esta es una pregunta demasiado general que es difícil de responder en un caso general. Aquí hay algunos pensamientos a lo largo de esta dirección general:
-
Una vez que cualquier parte de su resultado que está tratando de devolver de su función
A()
es asincrónico o utiliza cualquier operación asincrónica para obtener, la función en sí es asincrónica. En Javascript simple, nunca puede devolver un resultado asincrónico de forma sincrónica, por lo que su función debe usar un método asincrónico para devolver el resultado (promesa, devolución de llamada, evento, etc.). -
Cualquier función
B()
que llama a tu función asincrónicaA()
que también está tratando de devolver un resultado basado en lo que obtiene deA()
ahora también es asincrónico y también debe comunicar su resultado utilizando un mecanismo asincrónico. Este es true para una funciónC()
que llamaB()
y necesita comunicar su resultado a la persona que llama. Entonces, puede decir que el comportamiento asincrónico es contagioso. Hasta que llegue a algún punto de la cadena de llamadas en el que ya no necesite comunicar un resultado, todo tiene que utilizar mecanismos asincrónicos para comunicar el resultado, el error y la finalización. -
No hay necesidad específica de marcar una función.
async
a menos que necesite específicamente uno de los beneficios de unasync
función como la capacidad de utilizarawait
dentro de esa función o el manejo automático de errores que proporciona. Puede escribir funciones que devuelvan promesas sin problemas sin usarasync
en la declaración de función. Entonces, “NO”, no vuelvo a subir en la cadena de llamadas haciendo que todoasync
. Solo hago una función asincrónica si hay una razón específica para hacerlo. Por lo general, esa razón es que quiero usarawait
dentro de la función, pero también existe la captura automática de excepciones sincrónicas que se convierten en rechazos de promesa que describí anteriormente. Por lo general, no lo necesitaría con un código de buen comportamiento, pero a veces es útil con un código de mal comportamiento o un código con un comportamiento indefinido. -
await
también se usa solo cuando hay una razón específica para ello. No lo uso automáticamente en cada función que devuelve una promesa. He descrito anteriormente las razones para usarlo. Todavía se puede usar.then()
muy bien para procesar el resultado de una única llamada a función que devuelve una promesa. En algunos casos, es solo una cuestión de estilo personal si desea utilizar.then()
oawait
y no hay ninguna razón en particular para que tenga que ser de una forma u otra.
¿O tal vez debería acostumbrarme a llamar a todas mis funciones con una espera, independientemente de si son asíncronas o no?
¡Absolutamente no! En primer lugar, lo último que desea hacer es tomar un código perfectamente sincrónico y convertirlo innecesariamente en asincrónico o incluso hacerlo parecer asincrónico. Código asincrónico (incluso con async
y await
) es más complicado de escribir, depurar, comprender y mantener que el código síncrono, por lo que nunca querrá convertir innecesariamente código síncrono en código asincrónico agregando async/await
en ello:
Por ejemplo, nunca harías esto:
async function random(min, max)
let r = await Math.random();
return Math.floor((r * (max - min)) + min);
En primer lugar, esta es una operación perfectamente sincrónica que se puede codificar así:
function random(min, max)
let r = Math.random();
return Math.floor((r * (max - min)) + min);
Segundo, ese primero async
La implementación ha hecho que la función sea mucho más difícil de usar, ya que ahora tiene un resultado asincrónico:
random(1,10).then(r =>
console.log(r);
);
En lugar del simple uso sincrónico:
console.log(random(1,10));
async
/await
son solo el azúcar sintáctico, lo que significa que no aportan ninguna funcionalidad nueva al lenguaje, siendo solo envoltorios útiles para las promesas.
Si una función está marcada como async
, siempre devuelve una promesa:
> async function f() return 42;
undefined
> f()
Promise 42
Además, si una función es async
, usted puede await
por cualquier promesa (incluido el resultado de otra async
función) dentro de él y la ejecución del código de función se detendrá en await
hasta que esa promesa se resuelva o rechace.
Para responder a su pregunta: si usa una función de biblioteca, normalmente sabe si devuelve una promesa o no (y si está marcada como async
, seguramente lo hace). Así que asegúrate de await
para ello o usar .then
con la promesa devuelta.
Calificaciones y reseñas
Acuérdate de que puedes permitirte explicar si descubriste tu problema .