Bienvenido al segundo artículo de la serie de visualización del bucle de eventos de Node.js. En el primer artículo, aprendimos que el bucle de eventos es una parte crucial de Node.js que ayuda a orquestar la ejecución de código síncrono y asíncrono.

Recuerda que si quieres aprende NodeJS, te dejamos link al curso.

Consta de seis colas diferentes. Una cola nextTick y una cola promise (referidas como colas microtask en esta serie de posts), una cola timer, una cola I/O, una cola check, y finalmente una cola close.

En cada bucle, las funciones de devolución de llamada se ponen en cola cuando es apropiado y se ejecutan en la pila de llamadas. Para empezar este artículo, vamos a realizar algunos experimentos para asegurarnos de que nuestra visualización del bucle de eventos es correcta.

Para nuestro primer conjunto de experimentos, nos centraremos en la cola nextTick y la cola promise. Pero antes de sumergirnos en los experimentos, entendamos primero cómo podemos poner en cola una función callback en cada una de estas colas.

Puesta en cola de funciones callback


Para encolar una función callback en la cola nextTick, usamos el método incorporado process.nextTick(). La sintaxis es sencilla: process.nextTick(callbackFn). Cuando este método se ejecuta en la pila de llamadas, la función callback se encolará en la cola nextTick.

Para encolar una función callback en la cola de promesas, usaremos Promise.resolve().then(callbackFn) . Cuando la promesa se resuelva, la función pasada al bloque then() se pondrá en cola en la cola de promesas.

Ahora que ya sabemos cómo añadir funciones callback a ambas colas, empecemos con nuestro primer experimento.

Todos los experimentos se realizan con el formato de módulo CommonJS.

Experimento 1
Código

// index.js
console.log("console.log 1");
process.nextTick(() => console.log("this is process.nextTick 1"));
console.log("console.log 2");

Aquí, tenemos un trozo mínimo de código que registra tres sentencias diferentes. La segunda sentencia hace uso del método process.nextTick() para poner en cola una función callback en la cola nextTick.

Visualización

La primera sentencia console.log() se ejecuta introduciéndose en la pila de llamadas. Registra el mensaje correspondiente en la consola y luego se retira de la pila.

A continuación, process.nextTick() se ejecuta en la pila de llamadas. Esto pone en cola la función callback en la cola nextTick y se retira. Como todavía hay código escrito por el usuario para ejecutar, la función callback tiene que esperar su turno.

La ejecución avanza y la última sentencia console.log() es empujada a la pila. El mensaje se registra en la consola y la función sale de la pila. Ahora, no hay más código síncrono escrito por el usuario para ejecutar, por lo que el control entra en el bucle de eventos.

La función callback de la cola nextTick se coloca en la pila, console.log() se coloca en la pila, se ejecuta y el mensaje correspondiente se registra en la consola.

Si tu quieres aprender acerca de Node, recuerda que tenemos curso en

Inferencia


Todo el código JavaScript síncrono escrito por el usuario tiene prioridad sobre el código asíncrono que el tiempo de ejecución quiera ejecutar eventualmente.

Pasemos al segundo experimento.

Experimento 2

// index.js
Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
process.nextTick(() => console.log("this is process.nextTick 1"));

Tenemos una llamada a Promise.resolve().then() y una llamada a process.nextTick().

Cuando la pila de llamadas ejecuta la línea 1, pone en cola la función callback en la cola Promise.

Cuando la pila de llamadas ejecuta la línea 2, pone en cola la función callback en la cola nextTick.

No hay más código escrito por el usuario para ejecutar después de la línea 2.

El control entra en el bucle de eventos, donde la cola nextTick tiene prioridad sobre la cola promise (así es como funciona el tiempo de ejecución de Node.js).

El bucle de eventos ejecuta la función callback de la cola nextTick y luego la función callback de la cola promise.

La consola muestra "this is process.nextTick 1", y luego "this is Promise.resolve 1".

Inferencia


Todas las devoluciones de llamada de la cola nextTick se ejecutan antes que las devoluciones de llamada de la cola promise.

Permíteme que te guíe a través de una versión más elaborada del segundo experimento anterior.

Experimento extra
Código

// index.js
process.nextTick(() => console.log("this is process.nextTick 1"));
process.nextTick(() => {
  console.log("this is process.nextTick 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside next tick")
  );
});
process.nextTick(() => console.log("this is process.nextTick 3"));

Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
Promise.resolve().then(() => {
  console.log("this is Promise.resolve 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside Promise then block")
  );
});
Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

El código contiene tres llamadas a las sentencias process.nextTick() y tres llamadas a Promise.resolve(). Cada función callback registra un mensaje apropiado.

Sin embargo, el segundo process.nextTick(), y el segundo Promise.resolve() tienen una sentencia process.nextTick() adicional, cada una con una función callback.

Para acelerar la explicación de esta visualización, omitiré la pila de llamadas. Cuando la pila de llamadas ejecuta las seis sentencias, hay tres retrollamadas en la cola nextTick y tres en la cola promise. Sin nada más que ejecutar, el control entra en el bucle de eventos.

Como sabemos, la cola nextTick tiene prioridad. Se ejecuta la primera llamada de retorno y el mensaje correspondiente se registra en la consola.

A continuación, se ejecuta la segunda función callback, que registra la segunda sentencia log. Sin embargo, esta función callback contiene otra llamada a process.nextTick(), que pone en cola la sentencia de registro interna al final de la cola nextTick.

El nodo ejecuta entonces la tercera llamada de retorno nextTick registrando el mensaje correspondiente en la consola. Inicialmente, sólo había tres callbacks, pero el segundo callback añadió otro callback a la cola que ahora recibe su turno.

El bucle de eventos empuja la llamada de retorno interna nextTick, y se ejecuta la sentencia console.log().

La cola nextTick está vacía, y el control pasa a la cola promise. La cola de promesas es similar a la cola nextTick.

Primero se registra "Promise.resolve 1", seguido de "Promise.resolve 2". Sin embargo, se añade una función a la cola nextTick con una llamada a process.nextTick(). A pesar de esto, el control permanece en la cola de promesas y continúa ejecutando otras funciones callback. Entonces obtenemos Promise.resolve 3, y en este punto, la cola de promesas está vacía.

Node comprobará una vez más si hay nuevas callbacks en las colas de microtareas. Como hay una en la cola nextTick, la ejecuta, lo que resulta en nuestra última sentencia log.

Esto puede ser un experimento un poco avanzado, pero la inferencia sigue siendo la misma.

Si tu quieres aprender acerca de Qwik te dejamos el siguiente curso


Todas las llamadas de retorno en la cola nextTick son ejecutadas antes que todas las llamadas de retorno en la cola promise.

Ten cuidado cuando uses process.nextTick(). El uso excesivo de este método puede causar que el bucle de eventos se quede sin energía, impidiendo que el resto de la cola se ejecute. Incluso con un gran número de llamadas a nextTick(), se puede impedir que la cola de E/S ejecute sus propias retrollamadas.

La documentación oficial sugiere usar process.nextTick() por dos razones principales: para manejar errores o para permitir que una llamada de retorno se ejecute después de que la pila de llamadas se haya desenrollado pero antes de que el bucle de eventos continúe. Cuando uses process.nextTick(), asegúrate de usarlo con criterio.

Experimento extra


Los experimentos muestran que todo el código JavaScript síncrono escrito por el usuario tiene prioridad sobre el código asíncrono que el tiempo de ejecución quisiera ejecutar eventualmente, y que todas las retrollamadas en la cola nextTick se ejecutan antes que todas las retrollamadas en la cola promise.

Fuente