Las funciones asincrónicas te permiten escribir código basado en promesas como si fuera síncrono.
Las funciones asíncronas están habilitadas de forma predeterminada en Chrome, Edge, Firefox y Safari. son francamente maravillosas. Te permiten escribir código basado en promesas como si fuera síncrono, pero sin bloquear el subproceso principal. Hacen que tu código asíncrono menos “inteligente” sea más fácil de leer.
Las funciones asincrónicas funcionan de la siguiente manera:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
Si usas la palabra clave async
antes de una definición de función, puedes usar
await
dentro de la función. Cuando await
una promesa, se pausa la función.
de una manera sin bloqueos
hasta que se establezca la promesa. Si la promesa se cumple,
recuperar el valor. Si se rechaza la promesa, se arroja el valor rechazado.
Navegadores compatibles
Ejemplo: registra una recuperación
Supongamos que quieres recuperar una URL y registrar la respuesta como texto. Así se ve con promesas:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Y aquí se ve lo mismo con el uso de funciones asincrónicas:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
Es la misma cantidad de líneas, pero desaparecen todas las devoluciones de llamada. Esto hace que sea mucho más fáciles de leer, en especial para quienes no están familiarizados con las promesas.
Valores de retorno asíncronos
Las funciones asíncronas siempre muestran una promesa, ya sea que uses await
o no. Que
resuelve con lo que muestre la función asíncrona, o rechaza con
lo que arroje la función asíncrona. Entonces:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
... llamar a hello()
muestra una promesa que se cumple con "world"
.
async function foo() {
await wait(500);
throw Error('bar');
}
... llamar a foo()
muestra una promesa que se rechaza con Error('bar')
.
Ejemplo: transmitir una respuesta
El beneficio de las funciones asincrónicas aumenta en ejemplos más complejos. Digamos que querías para transmitir una respuesta mientras cierras sesión en los fragmentos y devolver el tamaño final.
Aquí está con promesas:
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
Mira, Jake "poseedor de promesas" Archibald. Mira cómo llamo
processResult()
dentro de sí mismo para configurar un bucle asíncrono. Una escritura que generó
Me siento muy inteligente. Pero como las empresas más "inteligentes" código, tienes que observarlo
años para descubrir qué está haciendo, como una de esas fotos estilo ojo mágico de
los años 90.
Intentémoslo de nuevo con funciones asíncronas:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
Todo lo "inteligente" ha desaparecido. El bucle asíncrono
que me hizo tan presumido
reemplaza por un confiable y aburrido bucle while. Mucho mejor. En el futuro, tendrás
iteradores asíncronos,
lo que
reemplaza el bucle while
por uno for-of para que sea aún más prolijo.
Otra sintaxis de función asíncrona
Ya te mostré async function() {}
, pero la palabra clave async
puede ser
que se usa con otra sintaxis de funciones:
Funciones de flecha
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
Métodos de objeto
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
Métodos de clase
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
Debe tener cuidado. Evita generar demasiadas secuencias
Si bien estás escribiendo un código que luce síncrono, asegúrate de no perderte la oportunidad de hacer cosas en paralelo.
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
Lo anterior tarda 1,000 ms en completarse, mientras que:
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
Lo anterior tarda 500 ms en completarse porque ambas esperas ocurren al mismo tiempo. Veamos un ejemplo práctico.
Ejemplo: muestra resultados de recuperaciones en orden
Supongamos que quieres recuperar una serie de URLs y registrarlas lo antes posible en la el orden correcto.
Respira profundo, así es como se ve eso con promesas:
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
Sí, así es, estoy usando reduce
para encadenar una secuencia de promesas. Soy así que
inteligentes. Pero esta es una codificación tan inteligente sin la cual es conveniente que no hagas nada.
Sin embargo, al convertir lo anterior en una función asíncrona, es tentador demasiado secuenciales:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
Solución de asistencia para el navegador: Generadores
Si orienta sus anuncios a navegadores que admiten generadores (que incluyen la versión más reciente de los navegadores principales ) puedes usar funciones asincrónicas de polyfill.
Babel lo hará por ti, este es un ejemplo mediante el REPL de Babel
- observa cuán similar es el código transpilado. Esta transformación forma parte del Ajuste predeterminado es2017 de Babel.
Recomiendo el enfoque transpilado, porque puedes desactivarlo cuando los navegadores objetivo admiten funciones asincrónicas, pero si realmente no deseas utilizar una transpilador, puedes usar Polyfill de Babel y usarla tú mismo. En lugar de esta sintaxis:
async function slowEcho(val) {
await wait(1000);
return val;
}
...incluirías el polyfill y escribe lo siguiente:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
Ten en cuenta que debes pasar un generador (function*
) a createAsyncFunction
,
y usa yield
en lugar de await
. Aparte de eso, funciona de la misma manera.
Solución alternativa: regenerador
Si segmentas tus anuncios para navegadores más antiguos, Babel también puede transpilar generadores, lo que te permite utilizar funciones asincrónicas hasta IE8. Para hacer esto, necesitas Ajuste predeterminado de Babel para es2017 y el ajuste predeterminado es2015.
El resultado no es tan bonito, así que ten cuidado con un exceso de código.
¡Haz todo asincrónico!
Una vez que las funciones asincrónicas llegan a todos los navegadores, úsalas en cada una función que devuelve promesas. No solo hacen que tu código sea más prolijo, sino que también que la función siempre muestre una promesa.
Me entusiasmé mucho con las funciones asincrónicas en otra 2014 y es genial verlos si llegan, de verdad, en navegadores. ¡Uy!