Node.js es una plataforma impulsada por eventos de un solo subproceso que es capaz de ejecutar programación asincrónica sin bloqueo. Estas funcionalidades de Node.js lo hacen eficiente en términos de memoria. El bucle de eventos permite que Node.js realice operaciones de E/S sin bloqueo a pesar de que JavaScript es de un solo subproceso. Se realiza asignando operaciones al sistema operativo siempre y cuando sea posible.
La mayoría de los sistemas operativos tienen subprocesos múltiples y, por lo tanto, pueden manejar múltiples operaciones que se ejecutan en segundo plano. Cuando se completa una de estas operaciones, el kernel le dice a Node.js y la devolución de llamada respectiva asignada a esa operación se agrega a la cola de eventos que eventualmente se ejecutará. Esto se explicará con más detalle más adelante en este tema.
Características del bucle de eventos:
- El bucle de eventos es un bucle sin fin, que espera tareas, las ejecuta y luego duerme hasta que recibe más tareas.
- El bucle de eventos ejecuta tareas desde la cola de eventos solo cuando la pila de llamadas está vacía, es decir, no hay una tarea en curso.
- El bucle de eventos nos permite usar devoluciones de llamada y promesas.
- El bucle de eventos ejecuta las tareas empezando por la más antigua.
Ejemplo:
console.log("This is the first statement"); setTimeout(function(){ console.log("This is the second statement"); }, 1000); console.log("This is the third statement");
Producción:
This is the first statement This is the third statement This is the second statement
Explicación: En el ejemplo anterior, la primera declaración de registro de la consola se envía a la pila de llamadas y «Esta es la primera declaración» se registra en la consola y la tarea se extrae de la pila. A continuación, setTimeout se envía a la cola y la tarea se envía al sistema operativo y se configura el temporizador para la tarea. Luego, esta tarea se extrae de la pila. A continuación, la tercera declaración de registro de la consola se envía a la pila de llamadas y «Esta es la tercera declaración» se registra en la consola y la tarea se extrae de la pila.
Cuando se agota el temporizador establecido por la función setTimeout (en este caso, 1000 ms), la devolución de llamada se envía a la cola de eventos. El bucle de eventos al encontrar la pila de llamadas vacía toma la tarea en la parte superior de la cola de eventos y la envía a la pila de llamadas. La función de devolución de llamada para la función setTimeout ejecuta la instrucción y «Esta es la segunda declaración» se registra en la consola y la tarea se extrae de la pila.
Nota: En el caso anterior, si el tiempo de espera se configuró en 0 ms, las declaraciones también se mostrarán en el mismo orden. Esto se debe a que aunque la devolución de llamada se envíe inmediatamente a la cola de eventos, el bucle de eventos no la enviará a la pila de llamadas a menos que la pila de llamadas esté vacía, es decir, hasta que finalice el script de entrada proporcionado.
Funcionamiento del bucle de eventos: cuando se inicia Node.js, inicializa el bucle de eventos, procesa el script de entrada proporcionado que puede realizar llamadas API asíncronas, programa temporizadores y luego comienza a procesar el bucle de eventos. En el ejemplo anterior, la secuencia de comandos de entrada inicial constaba de instrucciones console.log() y una función setTimeout() que programa un temporizador.
Cuando se usa Node.js, se usa un módulo de biblioteca especial llamado libuv para realizar operaciones asíncronas. Esta biblioteca también se usa, junto con la lógica posterior de Node, para administrar un grupo de subprocesos especial llamado grupo de subprocesos libuv. Este grupo de subprocesos se compone de cuatro subprocesos que se usan para delegar operaciones que son demasiado pesadas para el ciclo de eventos. Las operaciones de E/S, abrir y cerrar conexiones, setTimeouts son ejemplos de tales operaciones.
Cuando el grupo de subprocesos completa una tarea, se llama a una función de devolución de llamada que maneja el error (si lo hay) o realiza alguna otra operación. Esta función de devolución de llamada se envía a la cola de eventos. Cuando la pila de llamadas está vacía, el evento pasa por la cola de eventos y envía la devolución de llamada a la pila de llamadas.
El siguiente diagrama es una representación adecuada del bucle de eventos en un servidor Node.js:
Fases del bucle de eventos: el siguiente diagrama muestra una descripción general simplificada del orden de operaciones del bucle de eventos:
- Temporizadores: las devoluciones de llamadas programadas por setTimeout() o setInterval() se ejecutan en esta fase.
- Devoluciones de llamada pendientes: aquí se ejecutan las devoluciones de llamada de E/S diferidas hasta la siguiente iteración del bucle.
- Inactivo, Preparar: Usado internamente solamente.
- Sondeo: Recupera nuevos eventos de E/S.
- Verificar: Invoca devoluciones de llamada setIntermediate().
- Cerrar devoluciones de llamada: Maneja algunas devoluciones de llamada cercanas. Por ejemplo: socket.on(‘cerrar’, …)