Sección 3: JavaScript avanzado

Lección 20:

Cómo trabaja JavaScript

No necesitamos saber cómo funciona internamente JavaScript para escribir un programa. Pero es importante saber cómo lo hace. Es como ser un piloto y no saber cómo hace un avión para volar.

¿Qué es un programa? Un programa debe hacer algunas cosas sencillas: debe asignar un espacio de memoria en la computadora que lo ejecutará, ya que sino no podrá ni siquiera almacenar variables. Un programa también deberá interpretar y ejecutar código, lo cual implica leer, interpretar y ejecutar sentencias.

También sabemos de lecciones anteriores, que existe dentro de cada navegador web un motor de JavaScript que ejecuta nuestro programas escritos en ese lenguaje. Los motores de JavaScript están compuestos de dos partes, un espacio de memoria (memory heap) y una pila de llamadas (call stack).

Dentro del espacio de memoria es donde se almacenan los programas, y en la pila de llamadas es donde nuestro código es leído y ejecutado.

Cuando ejecutamos sentencias como:

const a = 1;

const b = 10;

const c = 100;

El motor de JavaScript lee una a una las sentencias y la ejecutas, luego de ejecutarlas guarda en un espacio de memoria los datos de cada variable.

¿Cuál es el problema que existe aquí? Debemos tener en cuenta la pérdida de memoria que podría existir. La cantidad de memoria disponible para almacenar estos datos es limitada.

Por lo tanto, al aumentar el número de variables, como puede ocurrir en aplicaciones web muy grandes, podríamos tener problemas, ya que la memoria podría resultarnos insuficiente. Es por ello que las variables globales son malas, porque si nos olvidamos de limpiarlas, nuestro espacio de memoria (memory heap) se llenará y el navegador web no podrá funcionar.

En una pila de llamadas (call stack) podemos tener un listado de sentencias como las siguientes:

console.log(‘1’);

console.log(‘2’);

console.log(‘3’);

Cuando ejecutamos este código obtendremos la siguiente salida:

1

2

3

Como dijimos, la pila de llamadas lee y ejecuta las sentencias de nuestro código. A medida que va leyendo las sentencias las va insertando en la pila y a medida que las va ejecutando, las va eliminando de la pila. Veamos un ejemplo más complejo para entender mejor cómo trabaja la pila de llamadas:

Ahora ejecutemos la función uno.

Vemos que obtenemos 4. ¿Qué pasó aquí de acuerdo con la pila de llamadas? Si tenemos una pila de llamadas, primero llamamos la función uno.

Vemos que la función uno se incorpora a la pila de llamadas. Luego la función uno llama a la función dos. Ahora la función dos se coloca encima en la pila de llamadas.

Dentro de la función dos tenemos la sentencia console.log(‘4’); la cual es incorporada en la cima de la pila.

Ahora la pila de llamadas al ver que ya no existe otra llamada dentro de la sentencia console.log(‘4’); la ejecuta, por lo que imprime en la consola el valor 4 y la remueve de la pila de llamadas.

Luego eliminar la función dos de la pila.

Ahora elimina la función uno de la pila, quedando vacía la pila de llamadas.

JavaScript es un lenguaje de un solo hilo que puede ser no bloqueante. De un solo hilo significa que solo tiene una pila de llamadas, por lo que solo puede hacer una cosa a la vez. En esta pila el primer elemento en ingresar es el último en salir, por lo que la pila ejecutar primero el elemento que se encuentra en su parte superior.

Otros lenguajes pueden tener múltiples pilas de llamadas y por eso se los denomina multihilos. ¿Por qué JavaScript fue diseñado para ser de un solo hilo? Ejecutar código en un solo hilo puede ser bastante fácil, ya que no se debe lidiar con escenarios complejos que surgen en entornos multihilo, ya que solo se posee una pila de llamadas de la cual ocuparse.

La programación sincrónica implica la ejecución lineal de cada una de las sentencias. Si tenemos por ejemplo el siguiente código:

La primera línea es ejecutada, y luego la segunda línea. La tercera línea se ejecuta cuando la segunda línea a terminado de ejecutarse.

Es posible que hayamos oído del desbordamiento de la pila. Además de ser un sitio web de mucho ayuda para todo desarrollador es una situación que deberíamos evitar cuando se ejecuten nuestros programas.

El desbordamiento de la pila ocurre cuando ésta se hace más y más grande hasta que simplemente no tiene suficiente espacio.

¿Podemos recrear un desbordamiento de pila? Si, veamos cómo hacerlo, gracias al siguiente código:

El código anterior parece muy confuso. Esto se llama recursividad, lo cual implica una función que se llama a sí misma. Por lo que en el ejemplo anterior la función foo se llama así misma una y otra vez, teniendo recurrencia, pero no teniendo un final a la vista, solo hasta que la pila sufre el desbordamiento.

Como mencionamos anteriormente, JavaScript posee un solo hilo, por lo que ejecuta sólo una sentencia a la vez.

Vemos el siguiente código:

¿Qué pasa si la segunda línea es una gran tarea que tenemos que hacer? Podría ser un array de millones de elementos que tengamos que recorrer. ¿Qué sucedería en ese caso? Para ser ejecutada la tercera sentencia tendría que esperar a que finalice toda la ejecución de la segunda sentencia.

En este pequeño ejemplo, eso no significa nada, pero si lo pasamos a un sitio web complejo, el usuario no podrá ser capaz que realizar cualquier acción dentro del sitio, ya que el sitio web se congelaría hasta que se termine una determinada tarea. Eso no sería bueno, ¿no?

Con la programación sincrónica, si tenemos una sentencia que toma mucho tiempo en ejecutarse, la siguiente sentencia tendrá que esperar. Necesitamos una forma de ejecutar nuestros programas sin que se bloqueen. ¿Cómo logramos esto? Por ello, surgió la programación asincrónica. Pensemos en asincrónico como un comportamiento.

La ejecución sincrónica es excelente porque es predecible. Sabemos lo que sucede primero, y luego lo que sucede a continuación. Pero la ejecución sincrónica puede ser lenta.

Entonces, cuando tenemos que hacer cosas como procesar imágenes, o realizar solicitudes a través de Internet, como las llamadas a una API, necesitamos algo más que solo tareas sincrónicas. ¿Cómo hacemos programación asincrónica? Por ejemplo, podemos hacer programación asincrónica añadiendo una sentencia como setTimeout.

setTimeout es una función que viene dentro de nuestros navegadores web, y nos permite crear un tiempo de espera. El primer parámetro de setTimeout es la función que queremos ejecutar, y el segundo parámetro es el tiempo que queremos esperar, expresado en milisegundos.

Ejecutemos este código en la consola y veamos qué sucede:

Bueno, eso fue todo por esta lección.

Nos vemos en la siguiente.

¡Adios!

Made with Slides.com