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.