"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Bucle de eventos

Bucle de eventos

Publicado el 2024-08-23
Navegar:998

Event Loop

Introducción
JavaScript se ejecuta principalmente en un solo subproceso en Node.js y en el navegador (con algunas excepciones, como los subprocesos de trabajo, que están fuera del alcance del artículo actual). En este artículo, intentaré explicar el mecanismo de concurrencia en Node.js, que es el Event Loop.

Antes de comenzar a leer este artículo, debes estar familiarizado con la pila y cómo funciona. Escribí en el pasado sobre esta idea, así que consulta Stack & Heap. No empieces a codificar sin comprenderlos. Moshe Binieli | Medio

Imagen de introducción
Ejemplos
Creo que aprender es mejor con ejemplos, por eso comenzaré con 4 ejemplos de código simples. Analizaré los ejemplos y luego me sumergiré en la arquitectura de Node.js.

Ejemplo 1:
consola.log(1);
consola.log(2);
consola.log(3);
// Producción:
// 1
// 2
// 3
Este ejemplo es bastante sencillo: en el primer paso, console.log(1) ingresa a la pila de llamadas, se ejecuta y luego se elimina; en el segundo paso, console.log(2) ingresa a la pila de llamadas y se ejecuta. y luego se elimina, y así sucesivamente para console.log(3).

Visualización de la pila de llamadas para el Ejemplo 1
Ejemplo 2:
consola.log(1);
setTimeout(función foo(){
consola.log(2);
}, 0);
consola.log(3);
// Producción:
// 1
// 3
// 2
Podemos ver en este ejemplo que ejecutamos setTimeout de inmediato, por lo que esperaríamos que console.log(2) estuviera antes que console.log(3), pero este no es el caso y comprendamos el mecanismo detrás de esto.

Arquitectura básica de bucle de eventos (profundizaremos más en ella más adelante)

Pila y montón: consulte mi artículo sobre este (agregué un enlace al principio de este artículo)
APIs web: están integradas en su navegador web y pueden exponer datos del navegador y del entorno informático circundante y hacer cosas complejas y útiles con ellos. No son parte del lenguaje JavaScript en sí, sino que están construidos sobre el lenguaje JavaScript principal, lo que le brinda superpoderes adicionales para usar en su código JavaScript. Por ejemplo, la API de geolocalización proporciona algunas construcciones de JavaScript simples para recuperar datos de ubicación para que pueda decir: trazar su ubicación en un mapa de Google. En segundo plano, el navegador en realidad utiliza algún código complejo de nivel inferior (por ejemplo, C) para comunicarse con el hardware GPS del dispositivo (o cualquier dispositivo disponible para determinar los datos de posición), recuperar datos de posición y devolverlos al entorno del navegador para su uso. en tu código. Pero nuevamente, la API le abstrae esta complejidad.
Bucle de eventos y cola de devolución de llamada: las funciones que finalizaron la ejecución de Web Apis se están moviendo a la cola de devolución de llamada, esta es una estructura de datos de cola normal, y el bucle de eventos es responsable de sacar de la cola la siguiente función de la cola de devolución de llamada y enviar la función a la pila de llamadas para ejecutar la función.
Orden de ejecución

Todas las funciones que se encuentran actualmente en la pila de llamadas se ejecutan y luego se eliminan de la pila de llamadas.
Cuando la pila de llamadas está vacía, todas las tareas en cola se colocan en la pila de llamadas una por una y se ejecutan, y luego se retiran de la pila de llamadas.
Entendamos el ejemplo 2

Se llama al método console.log(1), se coloca en la pila de llamadas y se ejecuta.

  1. El método setTimeout se llama y se coloca en la pila de llamadas y se ejecuta, esta ejecución crea una nueva llamada a setTimeout Web Api durante 0 milisegundos, cuando finaliza (de inmediato, o para ser más precisos, entonces sería mejor decir "tan pronto como sea posible"), la API web mueve la llamada a la cola de devolución de llamada.

  2. El método console.log(3) se llama y se coloca en la pila de llamadas y se ejecuta.

  3. El bucle de eventos ve que la pila de llamadas está vacía y saca el método "foo" de la cola de devolución de llamadas y lo coloca en la pila de llamadas, luego se ejecuta console.log(2).

Visualización del proceso para el Ejemplo 2
Por lo tanto, el parámetro de retardo en setTimeout(function, delay) no representa el retardo de tiempo preciso después del cual se ejecuta la función. Representa el tiempo mínimo de espera tras el cual en algún momento se ejecutará la función.

Ejemplo 3:
consola.log(1);
setTimeout (función foo() {
console.log('foo');
}, 3500);
setTimeout(función boo() {
console.log('boo');
}, 1000);
consola.log(2);
// Producción:
// 1
// 2
// 'abucheo'
// 'foo'

Visualización del proceso para el Ejemplo 3
Ejemplo 4:
consola.log(1);
setTimeout (función foo() {
console.log('foo');
}, 6500);
setTimeout(función boo() {
console.log('boo');
}, 2500);
setTimeout(función baz() {
consola.log('baz');
}, 0);
for (valor constante de ['A', 'B']) {
console.log(valor);
}
función dos() {
consola.log(2);
}
dos();
// Producción:
// 1
// 'A'
// 'B'
// 2
// 'baz'
// 'abucheo'
// 'foo'

Visualización del proceso para el Ejemplo 4
El bucle de eventos procede a ejecutar todas las devoluciones de llamada que esperan en la cola de tareas. Dentro de la cola de tareas, las tareas se clasifican en términos generales en dos categorías: microtareas y macrotareas.

Macrotareas (Cola de tareas) y Microtareas
Para ser más precisos, en realidad existen dos tipos de colas.

  1. La cola de macrotareas (o simplemente llamada cola de tareas).
  2. La cola de microtareas.

Hay algunas tareas más que se incluyen en la cola de macrotareas y en la cola de microtareas, pero cubriré las más comunes.

Las macrotareas comunes son setTimeout, setInterval y setImmediate.
Las microtareas comunes son Process.nextTick y Promise callback.
Orden de ejecución
Todas las funciones que se encuentran actualmente en la pila de llamadas se ejecutan y luego se eliminan de la pila de llamadas.
Cuando la pila de llamadas está vacía, todas las microtareas en cola se colocan en la pila de llamadas una por una y se ejecutan, y luego se retiran de la pila de llamadas.
Cuando tanto la cola de llamadas como la de microtareas están vacías, todas las macrotareas en cola se colocan en la pila de llamadas una por una y se ejecutan, y luego se retiran de la pila de llamadas.
Ejemplo 5:
consola.log(1);
setTimeout (función foo() {
console.log('foo');
}, 0);
Promesa.resolve()
.entonces(función boo() {
console.log('boo');
});
consola.log(2);
// Producción:
// 1
// 2
// 'abucheo'
// 'foo'
Se llama al método console.log(1), se coloca en la pila de llamadas y se ejecuta.
Se está ejecutando SetTimeout, console.log('foo') se mueve a SetTimeout Web Api y 0 milisegundos después se mueve a Macro-Task Queue.
Se llama a Promise.resolve(), se resuelve y luego el método .then() se mueve a la cola de Micro-Task.
Se llama al método console.log(2), se coloca en la pila de llamadas y se ejecuta.
Event Loop ve que la pila de llamadas está vacía, primero toma la tarea de la cola de Micro-Task, que es la tarea Promise, coloca el archivo console.log('boo') en la pila de llamadas y lo ejecuta.
Event Loop ve que la pila de llamadas está vacía, luego ve que la Micro-Task está vacía, luego toma la siguiente tarea de la cola de Macro-Task, que es la tarea SetTimeout, coloca console.log('foo') en la pila de llamadas y lo ejecuta.

Visualización del proceso para el Ejemplo 5
Comprensión avanzada del bucle de eventos
Estaba pensando en escribir sobre el nivel bajo de cómo funciona el mecanismo de Event Loop, podría ser un post en sí mismo, así que decidí traer una introducción al tema y adjuntar buenos enlaces que expliquen el tema en profundidad.

Explicación del nivel inferior del bucle de eventos
Cuando se inicia Node.js, inicializa el bucle de eventos, procesa el script de entrada proporcionado (o ingresa al REPL) que puede realizar llamadas API asíncronas, programar temporizadores o llamar a Process.nextTick() y luego comienza a procesar el bucle de eventos. &&&]

El siguiente diagrama muestra una descripción general simplificada del orden de operaciones del bucle de eventos. (Cada cuadro se denominará “fase” del ciclo del evento; consulte la imagen de introducción para comprender mejor el ciclo).

Descripción general simplificada del orden de operaciones del bucle de eventos

Cada fase tiene una cola FIFO de devoluciones de llamada para ejecutar (lo digo con cuidado aquí porque puede haber otra estructura de datos dependiendo de la implementación). Si bien cada fase es especial a su manera, generalmente, cuando el bucle de eventos ingresa a una fase determinada, realizará cualquier operación específica de esa fase y luego ejecutará devoluciones de llamada en la cola de esa fase hasta que la cola se haya agotado o se alcance el número máximo de devoluciones de llamada. ha ejecutado. Cuando la cola se agota o se alcanza el límite de devolución de llamadas, el bucle de eventos pasará a la siguiente fase, y así sucesivamente.

Resumen de las fases

Temporizadores: esta fase ejecuta callbacks programados por setTimeout() y setInterval().
Devoluciones de llamada pendientes: ejecuta devoluciones de llamada de E/S diferidas hasta la siguiente iteración del bucle.
Inactivo, Preparar: solo se usa internamente.
Encuesta: recuperar nuevos eventos de E/S; ejecutar devoluciones de llamada relacionadas con E/S (casi todas con la excepción de las devoluciones de llamada cerradas, las programadas por temporizadores y setImmediate()); El nodo se bloqueará aquí cuando sea apropiado.
Verificar: aquí se invocan las devoluciones de llamada setImmediate().
Cerrar devoluciones de llamada: algunas devoluciones de llamada cerradas, p. socket.on('cerrar', ...).
¿Cómo encajan aquí los pasos anteriores?
Entonces, los pasos anteriores solo con la “Cola de devolución de llamada” y luego con las “Colas macro y micro” fueron explicaciones abstractas sobre cómo funciona el bucle de eventos.

Hay otra cosa IMPORTANTE que mencionar: el bucle de eventos debe procesar la cola de microtareas por completo, después de procesar una macrotarea de la cola de macrotareas.

Paso 1: El bucle de eventos actualiza el tiempo del bucle a la hora actual para la ejecución actual.

Paso 2: Se ejecuta Micro-Queue.
Paso 3: Se ejecuta una tarea de la fase Temporizadores.
Paso 4: Comprobar si hay algo en la MicroCola y ejecutar toda la MicroCola si hay algo.
Paso 5: Vuelve al Paso 3 hasta que la fase de Temporizadores esté vacía.
Paso 6: Se ejecuta una tarea de la fase Devoluciones de llamadas pendientes.
Paso 7: Comprobar si hay algo en la MicroCola y ejecutar toda la MicroCola si hay algo.
Paso 8: Vuelve al Paso 6 hasta que la fase Devoluciones de llamada pendientes esté vacía.
Y luego Inactivo... Microcola... Encuesta... Microcola... Comprobar... Microcola... Cerrar CallBacks y luego comienza de nuevo.
Así que ofrecí una buena descripción general de cómo funciona realmente el bucle de eventos detrás de escena; faltan muchas partes que no mencioné aquí porque la documentación real está haciendo un gran trabajo al explicarlo. Proporcionaré excelentes enlaces para la documentación, le recomiendo que invierta entre 10 y 20 minutos y la comprenda.

Declaración de liberación Este artículo se reproduce en: https://dev.to/atulnagose1499/event-loop-4fck?1 Si hay alguna infracción, comuníquese con [email protected] para eliminarla.
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3