Ariel Rey

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á.

alt

Perdón, tenía que usar esta referencia 🤦‍♂️

Tenemos la solución

¿Homero Simpson tendría razón cuando dijo...?

alt

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:

  1. "Hello"
  2. "There"
  3. "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.

alt

  • Antes de empezar tendremos la consola vacía

alt

  • Ni bien comienza la ejecución, se imprime en la consola "Hello"

alt

  • 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".

alt

  • 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==.

alt

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:

alt

Antes de empezar, tendremos la consola vacía

alt

Se imprime "Hello" en la consola

alt

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".

alt

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.

alt

  • Antes de empezar nos encontramos con el Stack vacío

alt

  • Recuerden que el primer paso siempre es agregar un proceso al que llamamos main que representa a la ejecución del Script

alt

  • Luego agregamos al Stack la ejecución del console.log para imprimir la palabra "Hello"

alt

  • Una vez terminada la ejecución la removemos del Stack

alt

  • Ahora llega el momento de setTimeout. Procedemos a agregar la ejecución de la misma al Stack

alt

  • 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 🧙‍♂️

alt

  • Luego de no haber agregado a setTimeout al Stack, pasamos a la siguiente linea de código

alt

  • Agregamos al Stack la ejecución de console.log para imprimir "Bro" en la consola

alt

  • Una vez terminada la ejecución, lo removemos del Stack

alt

  • Terminada la ejecución del código, removemos a main del Stack. Pero, ¿que paso con el setTimeout? 🤷‍♂️

alt

  • Mágicamente y aproximadamente 5 segundos después, apareció en el Stack la ejecución del console.log para imprimir "There" en la consola

alt

  • 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.

alt

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.

alt

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

alt

  • Wow! 🤯. Ahora sí estamos hablando baby!. Como siempre, arrancamos con todo vacío.

alt

  • El primer paso es agregar el proceso main al Stack

alt

  • Luego agregamos la ejecución de console.log para imprimir "Hello" al Stack

alt

  • Una vez terminada la ejecución la removemos del Stack

alt

  • 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).

alt

  • 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 un timer con el Callback asociado.

alt

  • Una vez creado el timer removemos el setTimeout del Stack

alt

  • Agregamos al console.log al Stack que va a imprimir "Bro" en la consola

alt

  • Una vez terminada la ejecución, lo removemos del Stack

alt

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".

alt

  • Una vez concluidos los 5 segundos, el timer que se encuentra en la Web API mueve el Callback al Task Queue

alt

  • 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.

alt

  • El Callback es tomado por el Event Loop

alt

  • El Event Loop lo ingresa al Stack para ser ejecutado

alt

  • Una vez ejecutado el Callback, se agrega el console.log que imprime "There" en la consola

alt

  • Una vez terminada la ejecución del console.log se remueve del Stack

alt

  • Por último removemos el Callback del Stack

Entonces, ¿Qué es NodeJS?

alt

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.

alt

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.