Curso Básico de NodeJS - Parte 3 - El Event Loop
Hola 👋, mi nombre es Ari Rey y bienvenidos a esta tercera parte del Curso Básico de NodeJS sin ejecutar una linea de código. Pueden encontrar este mismo curso en mi canal de Youtube, y si les gusto, subscríbanse al canal y denle me gusta 👍 al video. Antes de empezar a leer esta Parte, les recuerdo leer la nota "¿Callbacks?, llámame cuando termines".
Tenemos un problema 👨🚀
Por si no lo recuerdan, finalizando la Parte 2 dijimos que el problema de bloquear el Call Stack, es que estábamos ejecutando código dentro del Navegador. Al bloquear el Call Stack estábamos bloqueando todas las interacciones dentro de la página. Puede ver el ejemplo acá.
Perdón, tenía que usar esta referencia 🤦♂️
Tenemos la solución
¿Homero Simpson tendría razón cuando dijo...?
Claro que no 🙄. 2 memes en menos de 100 palabras, perdón. La solución para no bloquear el Call Stack es ejecutar código de manera asincrónica, es decir, ==ejecutar código y no tener que esperar a que este termine para continuar==. Esto lo podemos hacer mediante el uso de Callbacks. Para entenderlo esto mejor, vamos a simular la ejecución del siguiente Script:
console.log('Hello');
setTimeout(function multiply(a, b) {
console.log('There');
}, 5000);
console.log('Bro');
El Script va a realizar 3 impresiones en consola:
- "Hello"
- "There"
- "Bro"
La impresión de "There" se encuentra contenida dentro de una función llamada setTimeout
. Esta función se encarga de ejecutar el código dentro de la misma pasadas una cantidad de milisegundos configurado por parámetro. En este caso va a realizar la impresión dentro de 5000 milisegundos que se traducen a 5 segundos.
A continuación veremos cómo se refleja la ejecución del Script en consola.
- Antes de empezar tendremos la consola vacía
- Ni bien comienza la ejecución, se imprime en la consola "Hello"
- Luego se imprime "Bro", pero, ¿qué paso?. El segundo
console.log
era "There" 😅. Deberían haber pasado 5 segundos y luego deberíamos haber visto "There".
- Por último se imprime "There", que era el segundo
console.log
Entonces, ¿qué paso?, ¿porque imprimió Bro
en lugar de There
en la consola?. Esto sucedió ya que ==setTimeout
es una función asincrónica== que recibe un primer parámetro llamado Callback. Para explicarlo de manera simple, ==el Callback es una función que se ejecuta una vez que la función asincrónica concluye su tarea==.
Recuerden leer la nota ¿Callbacks?, llámame cuando termines que escribí para explicar qué son y para que sirven los Callbacks.
Ahora, volvamos a simular la ejecución del Script:
Antes de empezar, tendremos la consola vacía
Se imprime "Hello" en la consola
Acá es donde se pone interesante 🤨. Si bien se ejecuta
setTimeout
, dijimos que era una función asincrónica y la misma iba a ejecutar su código una vez concluida su ejecución. Es decir, luego de 5 segundos. En el mientras tanto se continua con la ejecución y se imprime "Bro".
Luego de 5 segundos se ejecuta el Callback y se imprime "There" en la consola
El Callback ☎️ y el Stack 🔋
Recuerden mis jovenes Padawans, NodeJS es Single Thread y esto significa que tiene un solo Call Stack. ¿Entonces?, ¿cómo podría esto funcionar sin bloquear el Call Stack? 🤔. Simulemos la ejecución del mismo Script, pero ahora visualizando el Stack en lugar de la consola.
- Antes de empezar nos encontramos con el Stack vacío
- Recuerden que el primer paso siempre es agregar un proceso al que llamamos
main
que representa a la ejecución del Script
- Luego agregamos al Stack la ejecución del
console.log
para imprimir la palabra "Hello"
- Una vez terminada la ejecución la removemos del Stack
- Ahora llega el momento de
setTimeout
. Procedemos a agregar la ejecución de la misma al Stack
- Claro que no! 😡. Si hacemos esto, estaríamos agregando una función que "demora tiempo" al Stack y esto haría que se bloquee. ¿Entonces? Simple, no lo agregamos. Si alguien pregunta, un hechicero lo hizo 🧙♂️
- Luego de no haber agregado a
setTimeout
al Stack, pasamos a la siguiente linea de código
- Agregamos al Stack la ejecución de
console.log
para imprimir "Bro" en la consola
- Una vez terminada la ejecución, lo removemos del Stack
- Terminada la ejecución del código, removemos a
main
del Stack. Pero, ¿que paso con elsetTimeout
? 🤷♂️
- Mágicamente y aproximadamente 5 segundos después, apareció en el Stack la ejecución del
console.log
para imprimir "There" en la consola
- Una vez terminado, lo removemos del Stack
Hasta el momento, podemos decir que cuando se ejecuta una función asincrónica la vamos a eliminar del Stack para no bloquearlo y luego la vamos a volver a agregar una vez que termine.
El Event Loop
Ahora sí!, vamos a revelar el truco, a donde va a parar la ejecución de la función asincrónica.
Es aquí cuando aparece el Event Loop 😎. ¿Se acuerdan cuando les dije que NodeJS solo puede hacer una cosa a la vez? Bueno, esto no es 100% verdad 🤭. Si bien NodeJS cómo Runtime Environment solo puede ejecutar una cosa a la vez, el Navegador no es solo el Runtime Environment, recodemos el gráfico que vimos en la Parte 1.
Cómo pueden ver en la imagen, el Navegador nos provee de algo llamado Web APIs, que ==son efectivamente Hilos de ejecución que nos proveen de concurrencia==. Veamos el mismo ejemplo, pero con todos los elementos que componen el gráfico:
- Stack
- Web APIs
- Callback Queue
- Event Loop
- Wow! 🤯. Ahora sí estamos hablando baby!. Como siempre, arrancamos con todo vacío.
- El primer paso es agregar el proceso
main
al Stack
- Luego agregamos la ejecución de
console.log
para imprimir "Hello" al Stack
- Una vez terminada la ejecución la removemos del Stack
- Llego la ejecución del
setTimeout
y la agregamos al Stack como siempre.setTimeout
recibe 2 parámetros, el Callback y el delay (tiempo a esperar).
- Cómo les contaba previamente,
setTimeout
es una Web API que nos provee el Navegador y cómo podemos ver en el gráfico, no esta en el código de V8 😮. Entonces,setTimeout
termina creando untimer
con el Callback asociado.
- Una vez creado el
timer
removemos elsetTimeout
del Stack
- Agregamos al
console.log
al Stack que va a imprimir "Bro" en la consola
- Una vez terminada la ejecución, lo removemos del Stack
La Web API no puede agregar el Callback al Stack, si hace esto debería modificar el código. Es aquí cuando aparece el "Task Queue" ó en español "Cola de Tareas".
- Una vez concluidos los 5 segundos, el
timer
que se encuentra en la Web API mueve el Callback al Task Queue
- En este momento termino la ejecución del Script y removemos el proceso
main
del Stack
Finalmente ⏰, es aquí cuando aparece el Event Loop. Aleluya, alabado sea Dios, ¿WTF?. El Event Loop es la pieza más simple... ==Su única tarea es mirar el Stack y al Task Queue. Si él Stack esta vacío, toma el primer elemento del Task Queue y lo pasa al Stack para que este se ejecute==. Continuemos con el ejemplo.
- El Callback es tomado por el Event Loop
- El Event Loop lo ingresa al Stack para ser ejecutado
- Una vez ejecutado el Callback, se agrega el
console.log
que imprime "There" en la consola
- Una vez terminada la ejecución del
console.log
se remueve del Stack
- Por último removemos el Callback del Stack
Entonces, ¿Qué es NodeJS?
Luego de 3 partes y recordando todo lo que aprendimos, podríamos decir que:
NodeJS es un Javascript Runtime Environment que puede ejecutar múltiples tareas con un solo hilo de ejecución. Esto es posible mediante el uso de un modelo de comunicación de E/S no bloqueante por medio del código asincrónico (Callbacks) manejado por el Event Loop.
Sistema Operativo Vs. Navegador
Como les arranque diciendo en la Parte 2, iba a hablarles de V8 dentor del Navegador y luego les iba a explicar que cambia entre el V8 del Navegador y el V8 del Sistema Operativo. Entonces, ¿qué cambia?.
En lugar de tener Web APIs, vamos a tener las OS APIs escritas en C++ que van a ser las encargadas de ejecutar código asincrónico dentro del Sistema Operativo. Veamos como quedaría el siguiente gráfico.
Podemos ver que ahora ya no tenemos el DOM
, Window
, etc..., sino que vamos a tener librerías como fs
(file system) y path
entre otras.
¿El Final?
Con esta parte podemos dar por concluido el "Curso Básico de NodeJS". Si bien ya sabemos qué es NodeJS y entendemos la definición de la misma, estaré creando nuevas notas para complementar el mismo. Por ejemplo, próximamente una nueva nota sobre módulos y NPM 😉.
Creditos
Esta tercer parte del curso esta fuertemente inspirada en la charla que realizo Philip Roberts en la JSConf de 2014. Este video fue la piedra angular del curso y gracias a este video quise transmitir a quien pudiera (compañeros, amigos, etc...) que era realmente él Event Loop. Si se manejan bien con el Ingles no duden en verlo.