tag - script type= text/javascript src=
¿Es async aguarda realmente sin bloqueo en el navegador? (3)
He estado jugando con la función en un SPA utilizando TypeScript y Promesas nativas, y me doy cuenta de que incluso si refactorizo una función de larga duración en una función asíncrona que devuelve una promesa, la interfaz de usuario sigue sin responder.
Así que mis preguntas son:
¿Cómo ayuda exactamente la nueva función de async / await a evitar el bloqueo de la interfaz de usuario en el navegador? ¿Hay algún paso adicional especial que uno deba seguir 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 las funciones async anteriores, como setTimeout y XmlHttpRequest?
JavaScript es de un solo hilo y se ejecuta en el mismo hilo que la interfaz de usuario. Así que todo el código JavaScript bloqueará la interfaz de usuario. Como han mencionado otros, los trabajadores web pueden usarse para ejecutar código en otros subprocesos, pero tienen limitaciones.
La diferencia entre las funciones asíncronas y las regulares es que devuelven una promesa. Al utilizar una devolución de llamada, puede aplazar la ejecución del código, que controla el resultado de la invocación de una función y, por lo tanto, permite que la IU realice 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. El motivo 1 que se imprimió por última vez en los dos primeros casos es que las devoluciones de llamada para promesas no se ejecutan de inmediato.
await
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 se imprimirá hola, lo, 1. Al igual que una devolución de llamada para una promesa, el código después de await
nunca se ejecuta inmediatamente.
async
es una forma más elegante de estructurar código asíncrono. No permite nuevas capacidades; Es solo una mejor sintaxis que devoluciones de llamada o promesas.
Por lo tanto, async
no se puede utilizar para "hacer algo asíncrono". Si tiene un código que tiene que hacer un montón de procesamiento basado en la CPU, async
no va a hacer mágicamente que la interfaz de usuario responda. Lo que debería hacer es usar algo como worker , que son la herramienta adecuada para llevar el trabajo vinculado a la CPU a un hilo de fondo para que la interfaz de usuario responda.
await p
programa la ejecución del resto de su función cuando la promesa p
resuelve. Eso es todo.
async
te permite usar await
. Eso es (casi) todo lo que hace (también envuelve tu 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 el código.
Para una IU receptiva, descargue el trabajo intensivo de la CPU a un subproceso de worker y transmítale 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));