Buscar..
Introducción
async
y await
construir sobre promesas y generadores para expresar acciones en línea asíncronas. Esto hace que el código asíncrono o de devolución de llamada sea mucho más fácil de mantener.
Las funciones con la palabra clave async
devuelven una Promise
y pueden llamarse con esa sintaxis.
Dentro de una async function
la palabra clave await
puede aplicarse a cualquier Promise
, y hará que todo el cuerpo de la función después de la await
se ejecute después de que se resuelva la promesa.
Sintaxis
- función asíncrona foo () {
...
aguarda asyncCall ()
} - función asíncrona () {...}
- async () => {...}
- (async () => {
const data = esperar asyncCall ()
console.log (datos)}) ()
Observaciones
Las funciones asíncronas son un azúcar sintáctico sobre promesas y generadores. Le ayudan a hacer que su código sea más legible, fácil de mantener, más fácil de detectar errores y con menos niveles de sangría.
Introducción
Una función definida como async
es una función que puede realizar acciones asíncronas, pero aún así parece sincrónica. La forma en que se hace es utilizar el await
palabra clave para diferir la función mientras se espera una promesa para resolver o rechazar.
Nota: Las funciones asíncronas son una propuesta de Etapa 4 ("Finalizada") en curso para ser incluidas en el estándar ECMAScript 2017.
Por ejemplo, usando la API Fetch basada en promesa:
async function getJSON(url) {
try {
const response = await fetch(url);
return await response.json();
}
catch (err) {
// Rejections in the promise will get thrown here
console.error(err.message);
}
}
Una función asíncrona siempre devuelve una Promesa, por lo que puede usarla en otras funciones asíncronas.
Estilo de función de flecha
const getJSON = async url => {
const response = await fetch(url);
return await response.json();
}
Menos sangría
Con promesas:
function doTheThing() {
return doOneThing()
.then(doAnother)
.then(doSomeMore)
.catch(handleErrors)
}
Con funciones asíncronas:
async function doTheThing() {
try {
const one = await doOneThing();
const another = await doAnother(one);
return await doSomeMore(another);
} catch (err) {
handleErrors(err);
}
}
Observe cómo la devolución está en la parte inferior, y no en la parte superior, y utiliza la mecánica de manejo de errores nativa del idioma ( try/catch
).
Espera y precedencia del operador.
Debe tener en cuenta la prioridad del operador cuando utilice la palabra clave await
.
Imagina que tenemos una función asíncrona que llama a otra función asíncrona, getUnicorn()
que devuelve una Promesa que se resuelve en una instancia de la clase Unicorn
. Ahora queremos obtener el tamaño del unicornio utilizando el método getSize()
de esa clase.
Mira el siguiente código:
async function myAsyncFunction() {
await getUnicorn().getSize();
}
A primera vista, parece válido, pero no lo es. Debido a la precedencia del operador, es equivalente a lo siguiente:
async function myAsyncFunction() {
await (getUnicorn().getSize());
}
Aquí intentamos llamar al método getSize()
del objeto Promise, que no es lo que queremos.
En su lugar, deberíamos usar paréntesis para indicar que primero queremos esperar al unicornio, y luego llamar getSize()
método getSize()
del resultado:
async function asyncFunction() {
(await getUnicorn()).getSize();
}
Por supuesto. la versión anterior podría ser válida en algunos casos, por ejemplo, si la función getUnicorn()
era sincrónica, pero el método getSize()
era asíncrono.
Funciones asíncronas en comparación con las promesas
async
funciones async
no reemplazan el tipo de Promise
; agregan palabras clave de lenguaje que hacen que las promesas sean más fáciles de llamar. Son intercambiables:
async function doAsyncThing() { ... }
function doPromiseThing(input) { return new Promise((r, x) => ...); }
// Call with promise syntax
doAsyncThing()
.then(a => doPromiseThing(a))
.then(b => ...)
.catch(ex => ...);
// Call with await syntax
try {
const a = await doAsyncThing();
const b = await doPromiseThing(a);
...
}
catch(ex) { ... }
Cualquier función que use cadenas de promesas puede reescribirse usando await
:
function newUnicorn() {
return fetch('unicorn.json') // fetch unicorn.json from server
.then(responseCurrent => responseCurrent.json()) // parse the response as JSON
.then(unicorn =>
fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
})
)
.then(responseNew => responseNew.json())
.then(json => json.success) // return success property of response
.catch(err => console.log('Error creating unicorn:', err));
}
La función se puede reescribir usando async
/ await
siguiente manera:
async function newUnicorn() {
try {
const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
const unicorn = await responseCurrent.json(); // parse the response as JSON
const responseNew = await fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
});
const json = await responseNew.json();
return json.success // return success property of response
} catch (err) {
console.log('Error creating unicorn:', err);
}
}
Esta variante async
de newUnicorn()
parece devolver una Promise
, pero en realidad había varias palabras clave de await
. Cada uno devolvió una Promise
, así que en realidad teníamos una colección de promesas en lugar de una cadena.
De hecho, podemos considerarlo como un generador de function*
, cada uno de los cuales await
ser una yield new Promise
. Sin embargo, los resultados de cada promesa son necesarios para que la próxima continúe la función. Esta es la razón por la que se necesita la palabra clave adicional async
en la función (así como la palabra clave await
cuando se llaman las promesas), ya que le dice a Javascript que cree automáticamente un observador para esta iteración. La Promise
devuelta por la async function newUnicorn()
resuelve cuando se completa esta iteración.
Prácticamente, no necesitas considerar eso; await
oculta la promesa y async
oculta la iteración del generador.
Puede llamar a las funciones async
como si fueran promesas y await
cualquier promesa o función async
. No necesita await
una función asíncrona, del mismo modo que puede ejecutar una promesa sin un .then()
.
También puede utilizar un IIFE async
si desea ejecutar ese código inmediatamente:
(async () => {
await makeCoffee()
console.log('coffee is ready!')
})()
Looping con async espera
Al usar async en bucles, es posible que encuentre algunos de estos problemas.
Si solo intenta usar forEach
inside forEach
, se producirá un error de Unexpected token
.
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(e => {
const i = await somePromiseFn(e);
console.log(i);
});
})();
Esto viene del hecho de que erróneamente has visto la función de flecha como un bloque. La await
estará en el contexto de la función de devolución de llamada, que no es async
.
El intérprete nos protege de cometer el error anterior, pero si agrega async
a la forEach
llamada forEach
no se producen errores. Podría pensar que esto resuelve el problema, pero no funcionará como se esperaba.
Ejemplo:
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(async(e) => {
const i = await somePromiseFn(e);
console.log(i);
});
console.log('this will print first');
})();
Esto sucede porque la función async de devolución de llamada solo puede pausarse, no la función asíncrona primaria.
Podría escribir una función asyncForEach que devuelva una promesa y, a continuación, podría
await asyncForEach(async (e) => await somePromiseFn(e), data )
algo comoawait asyncForEach(async (e) => await somePromiseFn(e), data )
Básicamente, devuelve una promesa que se resuelve cuando se esperan y se hacen todas las devoluciones de llamada. Pero hay mejores maneras de hacer esto, y es simplemente usar un bucle.
Puede usar un bucle for-of
o un bucle for/while
while, en realidad no importa cuál elija.
(async() => {
data = [1, 2, 3, 4, 5];
for (let e of data) {
const i = await somePromiseFn(e);
console.log(i);
}
console.log('this will print last');
})();
Pero hay otra trampa. Esta solución esperará a que cada llamada a somePromiseFn
complete antes de iterar sobre la siguiente.
Esto es genial si realmente desea que sus invocaciones de somePromiseFn
se ejecuten en orden, pero si desea que se ejecuten simultáneamente, tendrá que await
en Promise.all
.
(async() => {
data = [1, 2, 3, 4, 5];
const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
console.log(...p);
})();
Promise.all
recibe una serie de promesas como único parámetro y devuelve una promesa. Cuando todas las promesas de la matriz se resuelven, la promesa devuelta también se resuelve. Nosotros await
con esa promesa y cuando se resuelva todos nuestros valores están disponibles.
Los ejemplos anteriores son totalmente ejecutables. La función somePromiseFn
se puede realizar como una función de eco asíncrono con un tiempo de espera. Puede probar los ejemplos en babel-repl con al menos el ajuste preestablecido de stage-3
y mirar la salida.
function somePromiseFn(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 250);
});
}
Operaciones asíncronas simultáneas (paralelas)
A menudo querrá realizar operaciones asíncronas en paralelo. Hay una sintaxis directa que admite esto en la propuesta de async
/ await
, pero como await
esperará una promesa, puede envolver varias promesas juntas en Promise.all
para esperarlas:
// Not in parallel
async function getFriendPosts(user) {
friendIds = await db.get("friends", {user}, {id: 1});
friendPosts = [];
for (let id in friendIds) {
friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
}
// etc.
}
Esto hará cada consulta para obtener las publicaciones de cada amigo en serie, pero se pueden hacer simultáneamente:
// In parallel
async function getFriendPosts(user) {
friendIds = await.db.get("friends", {user}, {id: 1});
friendPosts = await Promise.all( friendIds.map(id =>
db.get("posts", {user: id})
);
// etc.
}
Esto recorrerá la lista de ID para crear una serie de promesas. await
esperará a que todas las promesas estén completas. Promise.all
combina en una sola promesa, pero se hacen en paralelo.