Bienvenido al tercer artículo de nuestra serie sobre la visualización del bucle de eventos de Node.js. En el artículo anterior, exploramos las Colas de Microtareas y su orden de prioridad cuando se ejecuta código asíncrono. En este artículo, discutiremos la Timer Queue, que es otra cola en Node.js utilizada para manejar código asíncrono.
Antes de sumergirnos en la Timer Queue, recapitulemos rápidamente las Microtask Queues
. Para poner en cola una función callback
en la Microtask Queue
, usamos funciones como process.nextTick()
y Promise.resolve()
. La Microtask Queue
tiene la mayor prioridad cuando se trata de ejecutar código asíncrono en Node.js.
Puesta en cola de funciones callback
Pasemos ahora a la cola de temporizadores. Para poner en cola una función callback
en la Timer Queue
, podemos utilizar funciones como setTimeout
y setInterval
. Para el propósito de esta entrada, usaremos setTimeout
.
Para entender el orden de ejecución en la Timer Queue
, vamos a realizar una serie de experimentos. Pondremos en cola tareas tanto en la cola de microtareas como en la cola de temporizadores.
// index.js
setTimeout(() => console.log("this is setTimeout 1"), 0);
setTimeout(() => console.log("this is setTimeout 2"), 0);
setTimeout(() => console.log("this is setTimeout 3"), 0);
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 process.nextTick()
y tres llamadas a Promise.resolve()
y tres llamadas a setTimeout
. Cada función callback
registra un mensaje apropiado.
Las tres llamadas a setTimeout
tienen un retardo de 0ms
, lo que significa que las funciones callback
se ponen en cola tan pronto como cada sentencia setTimeout
se ejecuta en la pila de llamadas. El segundo process.nextTick()
, y el segundo Promise.resolve()
tienen una sentencia process.nextTick()
adicional, cada una con una función callback
.
Cuando la pila de llamadas ejecuta todas las sentencias, terminamos con tres callbacks
en la cola nextTick
, tres en la cola Promise
, y tres en la cola Timer
. No hay más código que ejecutar, y el control entra en el bucle de eventos.
La cola nextTick
tiene la prioridad más alta, seguida por la cola Promise
, y luego la cola Timer
. La primera llamada de retorno de la cola nextTick
se elimina de la cola y se ejecuta, registrando un mensaje en la consola.
A continuación, la segunda llamada de retorno se pone en cola y se ejecuta, lo que también registra un mensaje. La segunda llamada de retorno incluye una llamada a process.nextTick()
, que añade una nueva llamada de retorno a la cola nextTick
. La ejecución continúa, y la tercera llamada de retorno es retirada de la cola y ejecutada, registrando también un mensaje.
Finalmente, la nueva llamada de retorno añadida es retirada de la cola y ejecutada en la pila de llamadas, resultando en el cuarto mensaje de registro en la consola.
Cuando la cola nextTick
está vacía, el bucle de eventos pasa a la cola Promise
. La primera llamada de retorno es retirada de la cola y ejecutada en la pila de llamadas, imprimiendo un mensaje en la consola.
Si esttás aprendiendo Qwik, recuerda visitar le siguiente video
La segunda llamada de retorno tiene un efecto similar y también añade una llamada de retorno a la cola nextTick
. El tercer callback
en la Promise
se ejecuta, dando como resultado el siguiente mensaje de registro. En este punto, la cola Promis
está vacía, y el bucle de eventos comprueba la cola nextTick
en busca de nuevas retrollamadas. Encuentra una, que también se ejecuta registrando un mensaje en la consola.
Ahora, ambas colas de microtareas están vacías y el bucle de eventos pasa a la cola de temporizadores. Tenemos tres llamadas de retorno, y cada una de ellas es retirada de la cola y ejecutada en la pila de llamadas una a una.
Esto imprimirá "setTimeout 1", "setTimeout 2", y "setTimeout 3".
Las devoluciones de llamada en las colas de microtareas se ejecutan antes que las devoluciones de llamada en la cola de temporizadores.
Bien, hasta ahora, el orden de prioridad es la cola nextTick
, seguida por la cola Promise
, y luego la cola timer
. Pasemos ahora al siguiente experimento.
Experimento 4
Código
// index.js
setTimeout(() => console.log("this is setTimeout 1"), 0);
setTimeout(() => {
console.log("this is setTimeout 2");
process.nextTick(() =>
console.log("this is inner nextTick inside setTimeout")
);
}, 0);
setTimeout(() => console.log("this is setTimeout 3"), 0);
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 para el cuarto experimento será en su mayor parte el mismo que el del tercero, con una excepción. La función callback pasada a la segunda función setTimeout
ahora incluye una llamada a process.nextTick()
.
Apliquemos lo aprendido en el experimento anterior y avancemos hasta el punto en el que las llamadas de retorno de las colas de microtareas ya se han ejecutado. Supongamos que tenemos tres llamadas de retorno en la cola del temporizador.
La primera llamada de retorno es retirada de la cola y ejecutada en la pila de llamadas, resultando en un mensaje "setTimeout 1" impreso en la consola.
El bucle de eventos procede y ejecuta también la segunda llamada de retorno, lo que resulta en un mensaje "setTimeout 2" que se imprime en la consola. Sin embargo, esto también pone en cola una función callback
en la cola nextTick
.
Después de ejecutar cada callback
en la cola del temporizador, el bucle de eventos vuelve atrás y comprueba las colas de microtareas. Comprueba la cola nextTick
e identifica una llamada de retorno que necesita ser ejecutada.
Esta llamada de retorno es retirada de la cola y ejecutada en la pila de llamadas, resultando en el mensaje "inner nextTick" que se imprime en la consola.
Ahora que las colas de microtareas están vacías, el control vuelve a la cola del temporizador, y la última llamada de retorno se ejecuta dando como resultado un mensaje "setTimeout 3" en la consola.
Las retrollamadas en las colas de microtareas se ejecutan entre la ejecución de las retrollamadas en la cola del temporizador.
Experimento 5
Código
// index.js
setTimeout(() => console.log("this is setTimeout 1"), 1000);
setTimeout(() => console.log("this is setTimeout 2"), 500);
setTimeout(() => console.log("this is setTimeout 3"), 0);
El código contiene tres sentencias setTimeout
que ponen en cola tres funciones callback
diferentes. El primer setTimeout
tiene un retardo de 1000ms, el segundo tiene un retardo de 500ms, y el tercero tiene un retardo de 0ms. Las funciones callback
simplemente registran un mensaje en la consola cuando se ejecutan.
Cuando se realizan varias llamadas a setTimeout
, el bucle de eventos pone en cola la que tiene el retardo más corto y la ejecuta antes que las demás. Como resultado, observamos que "setTimeout 3" se ejecuta primero, seguido de "setTimeout 2" y luego de "setTimeout 1".
Las retrollamadas de la cola de temporizadores se ejecutan en orden FIFO (primero en entrar, primero en salir).
Conclusión
Los experimentos muestran que las retrollamadas de la cola de microtareas tienen mayor prioridad que las de la cola de temporizadores, y que las retrollamadas de la cola de microtareas se ejecutan entre la ejecución de las retrollamadas de la cola de temporizadores. La cola de temporizadores sigue el orden FIFO (primero en entrar, primero en salir).