Estoy pensando en eso y esto es lo que se me ocurrió:
Digamos que tenemos un código como este:
console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);
Entra una solicitud y el motor JS comienza a ejecutar el código anterior paso a paso. Las dos primeras llamadas son llamadas de sincronización. Pero cuando se trata de setTimeout
método, se convierte en una ejecución asíncrona. Pero JS regresa de inmediato y continúa ejecutando, lo que se llama Non-Blocking
o Async
. Y continúa trabajando en otros, etc.
Los resultados de esta ejecución son los siguientes:
acdb
Entonces, básicamente, el segundo setTimeout
se terminó primero y su función de devolución de llamada se ejecuta antes que el primero y eso tiene sentido.
Estamos hablando de la aplicación de un solo subproceso aquí. JS Engine sigue ejecutando esto y, a menos que finalice la primera solicitud, no irá a la segunda. Pero lo bueno es que no esperará setTimeout
a que se resuelvan las operaciones de bloqueo, por lo que será más rápido porque acepta las nuevas solicitudes entrantes.
Pero mis preguntas surgen en torno a los siguientes elementos:
# 1: Si estamos hablando de una aplicación de subproceso único, ¿qué mecanismo procesa setTimeouts
mientras el motor JS acepta más solicitudes y las ejecuta? ¿Cómo continúa el hilo único trabajando en otras solicitudes? Lo que funciona setTimeout
mientras otras solicitudes siguen llegando y se ejecutan.
# 2: Si estas setTimeout
funciones se ejecutan detrás de escena mientras entran y se ejecutan más solicitudes, ¿qué lleva a cabo las ejecuciones asíncronas detrás de escena? ¿Qué es esta cosa de la que hablamos llamada EventLoop
?
# 3: ¿ Pero no se debe poner todo el método EventLoop
para que todo se ejecute y se llame al método de devolución de llamada? Esto es lo que entiendo cuando hablo de las funciones de devolución de llamada:
function downloadFile(filePath, callback)
{
blah.downloadFile(filePath);
callback();
}
Pero en este caso, ¿cómo sabe JS Engine si se trata de una función asincrónica para poder poner la devolución de llamada en la EventLoop? Perhaps something like the
palabra clave async` en C # o algún tipo de atributo que indique que el método que JS Engine adoptará es un método asincrónico? y debe ser tratado en consecuencia.
# 4: Pero un artículo dice bastante contrario a lo que estaba adivinando sobre cómo podrían estar funcionando las cosas:
Event Loop es una cola de funciones de devolución de llamada. Cuando se ejecuta una función asíncrona, la función de devolución de llamada se inserta en la cola. El motor de JavaScript no comienza a procesar el bucle de eventos hasta que se ejecuta el código después de que se haya ejecutado una función asíncrona.
# 5: Y aquí está esta imagen que podría ser útil, pero la primera explicación en la imagen dice exactamente lo mismo mencionado en la pregunta número 4:
Entonces, mi pregunta aquí es obtener algunas aclaraciones sobre los elementos enumerados anteriormente.
Respuestas:
Solo hay 1 subproceso en el proceso de nodo que realmente ejecutará el JavaScript de su programa. Sin embargo, dentro del nodo en sí, en realidad hay varias operaciones de manejo de subprocesos del mecanismo de bucle de eventos, y esto incluye un grupo de subprocesos de E / S y algunos otros. La clave es que el número de estos subprocesos no corresponde al número de conexiones concurrentes que se manejan como lo harían en un modelo de concurrencia de subprocesos por conexión.
Ahora sobre "ejecutar setTimeouts", cuando invocas
setTimeout
, todo lo que hace el nodo es básicamente actualizar una estructura de datos de funciones que se ejecutarán en el futuro. Básicamente tiene un montón de colas de cosas que necesita hacer y cada "tic" del bucle de eventos selecciona uno, lo elimina de la cola y lo ejecuta.Una cosa clave para entender es que el nodo se basa en el sistema operativo para la mayor parte del trabajo pesado. Por lo tanto, el sistema operativo realiza un seguimiento de las solicitudes de red entrantes y, cuando el nodo está listo para manejar una, solo utiliza una llamada del sistema para solicitarle una solicitud de red con datos listos para ser procesados. Gran parte del nodo de "trabajo" de E / S es "Oye OS, ¿tiene una conexión de red con datos listos para leer?" o "Hola OS, ¿alguna de mis llamadas pendientes al sistema de archivos tiene datos listos?". Basado en su algoritmo interno y diseño del motor de bucle de eventos, el nodo seleccionará un "tic" de JavaScript para ejecutar, ejecutarlo y luego repetir el proceso nuevamente. Eso es lo que se entiende por el bucle de eventos. El nodo básicamente determina en todo momento "¿cuál es el próximo bit de JavaScript que debo ejecutar?", Luego lo ejecuta.
setTimeout
oprocess.nextTick
.No se ejecuta JavaScript detrás de escena. Todo el JavaScript en su programa se ejecuta al frente y al centro, uno a la vez. Lo que sucede detrás de escena es que el sistema operativo maneja la E / S y el nodo espera a que esté listo y el nodo administra su cola de javascript esperando para ejecutarse.
Hay un conjunto fijo de funciones en el núcleo del nodo que son asíncronas porque hacen llamadas al sistema y el nodo sabe cuáles son porque tienen que llamar al sistema operativo o C ++. Básicamente, todas las interacciones IO de red y sistema de archivos, así como las interacciones de procesos secundarios, serán asíncronas y la ÚNICA forma en que JavaScript puede hacer que el nodo ejecute algo de forma asincrónica es invocando una de las funciones asíncronas proporcionadas por la biblioteca central de nodos. Incluso si está utilizando un paquete npm que define su propia API, para producir el bucle de eventos, eventualmente el código del paquete npm llamará a una de las funciones asíncronas del núcleo del nodo y es cuando el nodo sabe que la marca está completa y puede iniciar el evento algoritmo de bucle de nuevo.
Sí, esto es cierto, pero es engañoso. La clave es que el patrón normal es:
Entonces, sí, podría bloquear totalmente el bucle de eventos simplemente contando los números de Fibonacci sincrónicamente todos en la memoria, todo en el mismo tic, y sí, eso congelaría totalmente su programa. Es concurrencia cooperativa. Cada tick de JavaScript debe producir el bucle de eventos dentro de un período de tiempo razonable o la arquitectura general falla.
fuente
process.nextTick
vssetTimeout
vssetImmediate
es sutilmente diferente, aunque realmente no debería tener que preocuparse. Tengo una publicación de blog llamada setTimeout y amigos que entra en más detalles.Hay un fantástico video tutorial de Philip Roberts, que explica el bucle de eventos de JavaScript de la manera más simple y conceptual. Todos los desarrolladores de JavaScript deberían echar un vistazo.
Aquí está el enlace del video en Youtube.
fuente
No piense que el proceso del host es de un solo subproceso, no lo son. Lo que es de un solo subproceso es la parte del proceso de host que ejecuta su código javascript.
Excepto para los trabajadores de fondo , pero estos complican el escenario ...
Por lo tanto, todo su código js se ejecuta en el mismo subproceso, y no hay posibilidad de que obtenga dos porciones diferentes de su código js para que se ejecuten simultáneamente (por lo tanto, no obtiene una pesadilla de concurrencia para administrar).
El código js que se está ejecutando es el último código que el proceso del host recogió del bucle de eventos. Básicamente, en su código puede hacer dos cosas: ejecutar instrucciones síncronas y programar funciones para que se ejecuten en el futuro, cuando ocurran algunos eventos.
Aquí está mi representación mental (cuidado: ¡es solo eso, no sé los detalles de implementación del navegador!) De su código de ejemplo:
Mientras se ejecuta el código, otro hilo en el proceso del host realiza un seguimiento de todos los eventos del sistema que están ocurriendo (clics en la interfaz de usuario, archivos leídos, paquetes de redes recibidos, etc.)
Cuando se completa el código, se elimina del bucle de eventos y el proceso del host vuelve a verificarlo para ver si hay más código para ejecutar. El bucle de eventos contiene dos controladores de eventos más: uno que se ejecutará ahora (la función justNow) y otro en un segundo (la función inAWhile).
El proceso de host ahora intenta hacer coincidir todos los eventos ocurridos para ver si hay controladores registrados para ellos. Descubrió que el evento que justNow está esperando ha sucedido, por lo que comienza a ejecutar su código. Cuando la función justNow sale, verifica el bucle de eventos otra vez, buscando controladores en los eventos. Suponiendo que ha pasado 1 s, ejecuta la función inAWhile, y así sucesivamente ...
fuente