En cuanto a subprocesos, ¿cuál es la diferencia entre los trabajadores web y las funciones declaradas como

async function xxx()
{
}

?

Sé que los trabajadores web se ejecutan en subprocesos separados, pero ¿qué pasa con las funciones asíncronas? ¿Se enhebran tales funciones de la misma manera que se ejecuta una función a través de setInterval, o están sujetas a otro tipo diferente de subprocesamiento?

21
resle 4 mar. 2018 a las 10:20

5 respuestas

La mejor respuesta

Las funciones Async son simplemente azúcar sintáctica alrededor de Promises y son envoltorios para Callbacks. Básicamente, cuando await algo, el motor JS continúa con otras cosas hasta que callback está esperando que le devuelvan las llamadas.

Que otro hilo esté involucrado depende de lo que esté esperando en la función async. Si es un temporizador (setTimeout), se establece un temporizador interno y el subproceso JS continúa con otras cosas hasta que finaliza el temporizador y luego continúa la ejecución.

Este comportamiento es algo similar con cada función que toma una devolución de llamada o devuelve un promise. Sin embargo, algunos de ellos, especialmente en el entorno Node.js (fetch, fs.readFile) comenzarán otro hilo internamente . Solo entrega algunos argumentos y recibe los resultados cuando finaliza el hilo. Sin embargo, con WebWorkers, controlas otro hilo directamente. Para shure puedes await acciones desde ese otro hilo también:

const workerDone = new Promise(res => window.onmessage = res);

(async function(){
    const result = await workerDone;
        //...
})();

TL; DR:

JS  <---> callbacks / promises <--> internal Thread / Webworker
10
Mike B. 21 may. 2019 a las 15:00

También se accede a los trabajadores mediante código asincrónico (es decir, Promesas), sin embargo, los trabajadores son una solución para las tareas intensivas de la CPU que bloquearían el hilo en el que se ejecuta el código JS; incluso si esta función intensiva de CPU se invoca de forma asincrónica.

Entonces, si tiene una función intensiva de CPU como renderThread(duration) y si le gusta

new Promise((v,x) => setTimeout(_ => (renderThread(500), v(1)),0)
    .then(v => console.log(v);
new Promise((v,x) => setTimeout(_ => (renderThread(100), v(2)),0)
    .then(v => console.log(v);

Incluso si el segundo tarda menos tiempo en completarse, solo se invocará después de que el primero libere el hilo de la CPU. Entonces obtendremos primero 1 y luego 2 en la consola.

Sin embargo, si estas dos funciones se hubieran ejecutado en Trabajadores separados, el resultado que esperamos sería 2 y 1, ya que podrían ejecutarse simultáneamente y el segundo finaliza y devuelve un mensaje antes.

Por lo tanto, para las operaciones IO básicas, el código asincrónico de un solo subproceso estándar es muy eficiente y la necesidad de Trabajadores surge de la necesidad de usar tareas que requieren mucha CPU y se pueden segmentar (asignar a múltiples Trabajadores a la vez) como FFT y otras cosas.

5
Redu 4 mar. 2018 a las 09:23

Quiero agregar mi propia respuesta a mi pregunta, con el entendimiento que reuní a través de todas las respuestas de otras personas:

En última instancia, todos menos los trabajadores web, son devoluciones de llamada glorificadas. El código en funciones asíncronas, funciones llamadas a través de promesas, funciones llamadas a través de setInterval y demás, todas se ejecutan en el hilo principal con un mecanismo similar al cambio de contexto. No existe paralelismo en absoluto.

La verdadera ejecución paralela con todas sus ventajas y desventajas, corresponde solo a los trabajadores web y a los trabajadores web.

(lástima - pensé que con "funciones asincrónicas" finalmente conseguimos un subproceso simplificado y "en línea")

2
resle 5 mar. 2018 a las 06:39

A diferencia de WebWorkers, nunca se garantiza que las funciones async se ejecuten en un hilo separado.

Simplemente no bloquean todo el hilo hasta que llega su respuesta. Puede pensar que están registrados como esperando un resultado, deje que se ejecute otro código y cuando llegue su respuesta se ejecutarán; de ahí el nombre de programación asíncrona .

Esto se logra a través de una cola de mensajes, que es una lista de mensajes para procesar. Cada mensaje tiene una función asociada que se llama para manejar el mensaje.

Haciendo esto:

setTimeout(() => {
    console.log('foo')
}, 1000)

Simplemente agregará la función de devolución de llamada (que se registra en la consola) a la cola de mensajes. Cuando transcurre el temporizador de 1000 ms, el mensaje se saca de la cola de mensajes y se ejecuta.

Mientras el temporizador está marcando, otro código es libre de ejecutar. Esto es lo que da la ilusión de multihilo.

El ejemplo setTimeout anterior usa devoluciones de llamada. Promises y async funcionan de la misma manera en un nivel inferior: se complementan con ese concepto de cola de mensajes, pero son sintácticamente diferentes.

7
Mike B. 21 may. 2019 a las 15:07

Aquí hay una manera de llamar a las funciones estándar como trabajadores, permitiendo un verdadero paralelismo. Es un truco impío escrito en sangre con la ayuda de Satanás, y probablemente haya un montón de peculiaridades del navegador que pueden romperlo, pero por lo que puedo decir, funciona.

[ restricciones : el encabezado de la función debe ser tan simple como función f (a, b, c) y si hay algún resultado, debe pasar por un declaración de devolución]

function Async(func, params, callback)
{ 
 // ACQUIRE ORIGINAL FUNCTION'S CODE
 var text = func.toString(); 


 // EXTRACT ARGUMENTS
 var args = text.slice(text.indexOf("(") + 1, text.indexOf(")")); 
 args     = args.split(",");
 for(arg of args) arg = arg.trim();


 // ALTER FUNCTION'S CODE:
 // 1) DECLARE ARGUMENTS AS VARIABLES
 // 2) REPLACE RETURN STATEMENTS WITH THREAD POSTMESSAGE AND TERMINATION
 var body = text.slice(text.indexOf("{") + 1, text.lastIndexOf("}")); 
 for(var i = 0, c = params.length; i<c; i++) body = "var " + args[i] + " = " + JSON.stringify(params[i]) + ";" + body;
 body = body + " self.close();"; 
 body = body.replace(/return\s+([^;]*);/g, 'self.postMessage($1); self.close();');


 // CREATE THE WORKER FROM FUNCTION'S ALTERED CODE
 var code   = URL.createObjectURL(new Blob([body], {type:"text/javascript"}));
 var thread = new Worker(code);


 // WHEN THE WORKER SENDS BACK A RESULT, CALLBACK AND TERMINATE THE THREAD
 thread.onmessage =
 function(result)
 {
  if(callback) callback(result.data);

  thread.terminate();
 }

}

Entonces, suponiendo que tenga esta función potencialmente intensiva en CPU ...

function HeavyWorkload(nx, ny) 
{
 var data = [];

 for(var x = 0; x < nx; x++)
 {
  data[x] = [];

  for(var y = 0; y < ny; y++)
  {
   data[x][y] = Math.random();
  }
 }

 return data;
}

... ahora puedes llamarlo así:

Async(HeavyWorkload, [1000, 1000],
function(result)
{
 console.log(result);
}
);
3
resle 11 mar. 2018 a las 05:48