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 como await 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.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow