¿Async Await realmente no bloquea en el navegador?

7 minutos de lectura

avatar de usuario de prmph
prmph

He estado jugando con la función en un SPA usando TypeScript y promesas nativas, y noté que incluso si refactorizo ​​una función de ejecución prolongada en una función asíncrona que devuelve una promesa, la interfaz de usuario sigue sin responder.

Entonces mis preguntas son:

  • ¿Cómo ayuda exactamente la nueva función async/await a evitar el bloqueo de la interfaz de usuario en el navegador? ¿Hay algún paso adicional especial que uno deba tomar al usar async/await para obtener una interfaz de usuario receptiva?

  • ¿Alguien puede crear un violín para demostrar cómo async/await ayuda a que la interfaz de usuario responda?

  • ¿Cómo se relaciona async/await con funciones asíncronas anteriores, como setTimeout y XmlHttpRequest?

  • El código que bloquea seguirá bloqueando. Si no fuera así, podrías tener carreras de datos. La idea de las funciones asíncronas es que puede detenerse en el lugar para esperar la ejecución posterior del código asíncrono. Así que te detienes mientras esperas que se complete algo asíncrono, como un setTimeoutuna respuesta XHR o un evento de clic: jsfiddle.net/wgqyayhr (La demostración necesita un navegador con soporte)

    usuario1106925

    13/03/2017 a las 21:54


  • async/await es no parte de ES7 (ES2016). Será parte del lanzamiento de este año, ES2017.

    – Félix Kling

    16 de marzo de 2017 a las 6:18

avatar de usuario de jib
foque

await p programa la ejecución del resto de su función cuando promete p resuelve Eso es todo.

async te permite usar await. Eso es (casi) todo lo que hace (también envuelve su resultado en una promesa).

Juntos, hacen que el código sin bloqueo se lea como un código de bloqueo más simple. No desbloquean código.

Para una interfaz de usuario receptiva, descargue el trabajo intensivo de la CPU a un obrero hilo, y pasarle mensajes:

async function brutePrime(n) {
  function work({data}) {
    while (true) {
      let d = 2;
      for (; d < data; d++) {
        if (data % d == 0) break;
      }
      if (d == data) return self.postMessage(data);
      data++;
    }
  }

  let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
  let worker = new Worker(URL.createObjectURL(b));
  worker.postMessage(n); 
  return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}

(async () => {
  let n = 700000000;
  for (let i = 0; i < 10; i++) {
    console.log(n = await brutePrime(n + 1));
  }
})().catch(e => console.log(e));

  • Puntos para el trabajador sin expediente externo. Ese es un truco genial.

    – solarc

    19 de julio de 2017 a las 22:44

  • las funciones asíncronas también devuelven una promesa

    – código

    25 de junio de 2020 a las 16:37

  • “await p programa la ejecución del resto de su función cuando se resuelve la promesa p. Eso es todo”. –> La mejor y más concisa explicación de async/await que he leído en todo Internet.

    – rsp1984

    20 de marzo de 2022 a las 17:10

async es una forma más elegante de estructurar el código asíncrono. No permite nuevas capacidades; es solo una sintaxis mejor que las devoluciones de llamada o las promesas.

Entonces, async no se puede usar para “hacer algo asíncrono”. Si tiene código que tiene que hacer mucho procesamiento basado en CPU, async no va a hacer mágicamente que la interfaz de usuario responda. Lo que tendrías que hacer es usar algo como trabajadores webcual son la herramienta adecuada para enviar el trabajo vinculado a la CPU a un subproceso en segundo plano para que la interfaz de usuario responda.

  • Supongo que podrías usarlo así, para detener el bloqueo de un bucle. for (let i = 0; i setTimeout(resolución, ms)); }

    – Will Voelcker

    18 de diciembre de 2017 a las 13:03

  • @ Martian2049: Sí; async se basa en Promesas, que no bloquean.

    – Stephen Cleary

    24 de mayo de 2018 a las 15:03

  • “No permite nuevas capacidades” En realidad lo hace. “Entonces, asíncrono no se puede usar para” hacer algo asíncrono “ Pero await poder.

    – un mejor oliver

    1 de noviembre de 2018 a las 11:16

  • @abetteroliver: Tal vez estamos viendo diferentes semánticas. Cuando yo digo “[async] no permite nuevas capacidades”, quiero decir que todo lo que puedes hacer con async se puede hacer sin async usando devoluciones de llamada/promesas, lo cual es trivialmente demostrable porque hasta hace poco async fue una transformación de código en tiempo de compilación sin soporte de tiempo de ejecución.

    – Stephen Cleary

    2 de noviembre de 2018 a las 13:01


  • async ha estado disponible desde hace bastante tiempo y la transformación a la que te refieres utiliza generadores. Tanto las funciones del generador como las funciones asíncronas preservan la pila y esencialmente permiten pausar las funciones. No puedes lograr eso usando promesas. Tienes razón, por supuesto, eso await no hace que una API sea asíncrona, pero debido a la naturaleza asíncrona de las promesas (nativas), otro código puede (!) Tener la oportunidad de ejecutarse antes de que la función continúe.

    – un mejor oliver

    10 dic 2018 a las 20:15

JavaScript es de subproceso único y se ejecuta en el mismo subproceso que la interfaz de usuario. Entonces, todo el código JavaScript bloqueará la interfaz de usuario. Como mencionaron otros trabajadores web, se pueden usar para ejecutar código en otros subprocesos, pero tienen limitaciones.

La diferencia entre las funciones asíncronas y las normales es que devuelven una promesa. Usando una devolución de llamada, puede diferir la ejecución del código, que maneja el resultado de la invocación de una función y, por lo tanto, permite que la interfaz de usuario haga algún trabajo. Los siguientes tres ejemplos tienen el mismo efecto:

async function foo() {
  console.log("hi");
  return 1; 
}
foo().then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");

En los tres casos, la consola registra hi, lo, 1. Antes de que se imprima 1, la interfaz de usuario puede manejar la entrada del usuario o dibujar actualizaciones. La razón 1 que se imprime en último lugar en los dos primeros casos es que las devoluciones de llamadas para promesas no se ejecutan de inmediato.

await le permite hacer eso sin devoluciones de llamada:

async function foo() {
  console.log("hi");
  return 1; 
}

async function bar() {
  const result = await foo();
  console.log(result);
}

bar();
console.log("lo"); 

Eso también imprimirá hola, lo, 1. Al igual que una devolución de llamada para una promesa, el código después await nunca se ejecuta inmediatamente.

  • ¿Qué pasa si esperas bar() ?

    – Rodrigo

    25 oct 2018 a las 16:43

  • @Rodrigo Solo puede esperar dentro de las funciones asíncronas.

    – un mejor oliver

    1 de noviembre de 2018 a las 11:08

  • Por que hi imprímete antes lo?

    – zzzzzzzz

    29 de marzo de 2019 a las 15:18

  • @zzzzzzz bar se llama antes console.log("lo") y llama foo que llama console.log("hi").

    – un mejor oliver

    30 de marzo de 2019 a las 16:14

  • @zzzzzzz Las funciones asíncronas devuelven una promesa. await espera a que la promesa sea resuelta o rechazada y eso siempre ocurre de forma asíncrona. No hay tiempo de espera en un sentido estricto, es solo que cualquier código síncrono se ejecuta antes de eso.

    – un mejor oliver

    8 de abril de 2019 a las 17:18

Está claro a partir de la descripción en desarrollador.mozilla.org que no es bloqueante:

La palabra clave await hace que el tiempo de ejecución de JavaScript pause su código en esta línea, permitiendo que otro código se ejecute mientras tanto (Nota: mi negrita), hasta que la llamada a la función asíncrona haya devuelto su resultado. Una vez que se completa, su código continúa ejecutándose a partir de la siguiente línea.

Llego tarde a la fiesta aquí. Quería verificar los casos de uso síncrono y asíncrono.

Para ser claros, async/await no crea código síncrono. solo agrega syntactic sugar eso hace que el código parezca sincrónico. Debajo de los envoltorios, la lógica Promise continúa implementándose non-preemptive multitasking.

en el ejemplo gist puede ejecutar el ejemplo con un parámetro de línea de comandos que seleccione la entrada CLI de bloqueo o no bloqueo.
asyncExample.js

¿Ha sido útil esta solución?