Entendiendo JavaScript: Micro y Macro Tasks

Entendiendo JavaScript: Micro y Macro Tasks

¡Hola, equipo! Si eres un desarrollador web o simplemente alguien curioso sobre la programación, seguramente has escuchado hablar de JavaScript. Es un lenguaje poderoso que impulsa la web y permite que los sitios interactúen contigo de manera dinámica. Pero, ¿has oído hablar de las micro y macro tasks en JavaScript? ¿Y sabes cómo la función queueMicrotask() encaja en este escenario? No te preocupes si esto suena complicado por ahora, porque en este artículo vamos a desentrañar estos conceptos de una forma relajada y divertida.

¡Vamos a ello!

La Magia de JavaScript

La Magia de JavaScript Antes de adentrarnos en las tareas (tasks), es importante entender JavaScript a un nivel más básico. JavaScript es un lenguaje de programación ampliamente utilizado para crear interacciones dinámicas en sitios y aplicaciones web. Te permite hacer cosas increíbles como animaciones suaves, actualizaciones de página sin recargar e incluso juegos en línea.

Una característica fundamental de JavaScript es que es single-threaded, lo que significa que procesa una cosa a la vez. Imagina que es como un operador de telemarketing (no, no odies a JavaScript por eso) que solo puede atender una llamada a la vez. Esto puede parecer limitante, pero JavaScript usa un mecanismo llamado event loop para manejar tareas concurrentes de manera inteligente.

El Event Loop

El event loop es el cerebro detrás del funcionamiento asíncrono de JavaScript. Permite que JavaScript ejecute tareas en segundo plano mientras sigue respondiendo a eventos del usuario y haciendo otras cosas. Imagina a un chef que está cocinando varias recetas al mismo tiempo, pero siempre revisa la fila de pedidos para ver si hay algo nuevo que hacer.

Sin embargo, para entender completamente el event loop, necesitamos echar un vistazo más de cerca a cómo se organizan las tareas en JavaScript.

Micro Tasks y Macro Tasks

En JavaScript, tenemos dos tipos principales de tareas que desempeñan un papel fundamental en la gestión del código asíncrono: las micro tasks y las macro tasks. Vamos a explorar qué son estas tareas y cómo funcionan en el contexto de JavaScript.

En JavaScript, existen dos tipos principales de tareas que juegan un papel fundamental en la gestión de código asincrónico: micro tasks y macro tasks. Exploremos cuáles son estas tareas y cómo funcionan en el contexto de JavaScript.

Micro Tasks

Una micro task, como la define MDN, es una función corta que se ejecuta después de que finaliza la función o programa que la generó, pero antes de devolver el control al event loop. Lo más destacado de las micro tasks es su alta prioridad, ya que se ejecutan inmediatamente cuando la pila de ejecución de JavaScript (call stack) está vacía. Estas se colocan en la fila de micro tasks para su ejecución posterior.

Veamos un ejemplo de código que utiliza una micro task:

console.log('Início do código');

Promise.resolve().then(() => {
  console.log('Micro Task executada!');
});

console.log('Fim do código');

En este ejemplo, la micro task se agenda para ejecutarse después de que se hayan ejecutado las demás instrucciones. Por lo tanto, la salida será la siguiente:

Início do código
Fim do código
Micro Task executada!

Macro Tasks

Las macro tasks, también conocidas simplemente como tasks, representan otra categoría esencial de tareas en JavaScript. Una macro task es cualquier código programado para ser ejecutado por el motor estándar de JavaScript, como el inicio de la ejecución de un programa, eventos que activan callbacks, timeouts e intervalos. Las macro tasks se añaden a la fila de tareas (macro tasks queue).

Al ejecutar tareas de la fila de tareas, el runtime ejecuta cada tarea que esté en la fila cuando comienza una nueva iteración del event loop. Las tareas añadidas a la fila después del inicio de la iteración no se ejecutarán hasta la siguiente iteración.

Demostremos el uso de una macro task con un ejemplo simple:

console.log('Início do código');

setTimeout(() => {
  console.log('Macro Task executada!');
}, 0);

console.log('Fim do código');

En este ejemplo, estamos usando setTimeout para añadir una macro task que se ejecutará después de un tiempo de espera de 0 milisegundos. Aunque el tiempo de espera sea cero, la macro task se trata como una tarea de menor prioridad y se coloca al final de la fila. La salida será la siguiente:

Início do código
Fim do código
Macro Task executada!

Función queueMicrotask()

Ahora que ya entendemos los conceptos de micro tasks y macro tasks, vamos introducir la función queueMicrotask(). Esta función ofrece una manera explícita de programar micro tasks en tu código JavaScript. Acepta una función como argumento y la añade a la fila de micro tasks para que se ejecute cuando la pila de llamadas de funciones esté vacía.

Sin embargo, es crucial mencionar que la función queueMicrotask() debe usarse con cuidado. Programar demasiadas micro tasks puede causar un comportamiento inesperado y afectar negativamente el rendimiento de tu aplicación. Por lo tanto, es fundamental aplicar esta función con discernimiento, reservándola para situaciones en las que sea necesario asegurar que una micro task se ejecute inmediatamente después de las operaciones actuales.

Aquí tienes un ejemplo de cómo usar queueMicrotask():

console.log('Início do código');

queueMicrotask(() => {
  console.log('Micro Task agendada com queueMicrotask()!');
});

console.log('Fim do código');

En este ejemplo, utilizamos queueMicrotask() para programar una micro task que se ejecutará después de las otras instrucciones. La salida será la siguiente:

Início do código
Fim do código
Micro Task agendada com queueMicrotask()!

La función queueMicrotask() es útil cuando necesitas garantizar que una micro task se ejecute inmediatamente después de la finalización de las operaciones actuales, sin importar el contexto en el que estés trabajando.

Comportamiento de ejecución de Micro Tasks y Macro Tasks

La diferencia en el comportamiento de ejecución entre micro tasks y macro tasks es un aspecto crítico que debes entender. Es importante saber que, después de cada macro task, el event loop verifica si está devolviendo el control al código JavaScript. Si no es así, todas las micro tasks pendientes se ejecutan antes de considerar otra macro task.

Esta peculiaridad asegura que las micro tasks tengan prioridad y se ejecuten inmediatamente, lo que las convierte en ideales para tareas críticas que requieren una respuesta rápida. Sin embargo, esto también puede ser peligroso, ya que si al procesar las micro tasks sigues añadiendo más a la fila, podrías bloquear tu código principal.

Pero ahora que entendemos lo básico de las micro tasks, macro tasks y la función queueMicrotask(), echemos un vistazo a un ejemplo práctico que muestra cómo funcionan junto con el event loop.

console.log('Início do código');
let i;

for (i = 0; i < 5; i++) {

  setTimeout(() => {
    console.log('i:', i);
  }, 0);

}

Promise.resolve().then(() => {
  console.log('Micro Task executada!');
});

queueMicrotask(() => {
  console.log('Micro Task agendada com queueMicrotask()!');
});

console.log('Fim do código');

En este ejemplo, verás algo interesante. El loop está configurado para crear cinco setTimeouts y dos micro tasks (la Promise.resolve() y queueMicrotask()).

Aquí está lo que ocurre paso a paso:

  1. El código comienza a ejecutarse e imprime "Inicio del código".
  2. Se declara una variable i en el ámbito global.
  3. Luego, el código entra en un loop que va de 0 a 4 (5 veces en total) usando la variable i como contador.
  4. Dentro del loop, se llama a la función setTimeout cinco veces. Cada llamada a setTimeout programa una macro task que se ejecutará tras un tiempo de espera de 0 milisegundos. Esta función imprimirá "i:" seguido del valor actual de i.
  5. El loop sigue ejecutándose e incrementando i en cada iteración hasta que alcanza el valor de 5.
  6. Después de terminar el loop, la variable i ahora tiene el valor 5.
  7. Justo después del loop for, hay una llamada a Promise.resolve().then(). Lo que crea una micro task que imprimirá "¡Micro task ejecutada!".
  8. La función queueMicrotask() también se llama, programando otra micro task para imprimir "¡Micro task programada con queueMicrotask()!".
  9. En este punto, la ejecución del código principal ha terminado y se imprime "Fin del código".

Aquí está lo que sucede con respecto a las micro tasks y macro tasks:

  • Durante el loop for, cinco macro tasks son programadas con setTimeout. No se ejecutan de inmediato.
  • Se programan dos micro tasks: una con la promesa y otra con queueMicrotask(). Recuerda que las micro tasks tienen prioridad sobre las macro tasks.

Ahora, el orden de ejecución es el siguiente:

  1. La pila de llamadas está vacía, por lo que el event loop verifica la fila de micro tasks. La primera micro task (la promesa) se ejecuta e imprime "¡Micro task ejecutada!".
  2. La segunda micro task, programada con queueMicrotask(), se ejecuta e imprime "¡Micro task programada con queueMicrotask()!".
  3. Con todas las micro tasks procesadas, el event loop vuelve a verificar la fila de macro tasks.
  4. Las cinco macro tasks programadas con setTimeouts ahora se ejecutan. Sin embargo, el valor de i es 5 para todas ellas, ya que el loop terminó y i fue incrementado hasta 5.
  5. "i: 5" se imprime cinco veces, una vez por cada macro task.

La salida final será algo parecido a esto:

Início do código
Fim do código
Micro Task executada!
Micro Task agendada com queueMicrotask()!
i: 5
i: 5
i: 5
i: 5
i: 5

Este ejemplo ilustra cómo las micro tasks tienen prioridad sobre las macro tasks y cómo el orden de ejecución puede afectar el resultado final, especialmente en situaciones donde variables como i están involucradas. Asegúrate de comprender estos conceptos al trabajar con código asíncrono en JavaScript para evitar sorpresas.

¡Al infinito y más allá!

¡Y ahí lo tienes, equipo! Ahora tienes una comprensión más profunda de cómo JavaScript maneja tareas asíncronas usando micro tasks y macro tasks, y cómo la función queueMicrotask() puede usarse para programar micro tasks explícitamente. El event loop es lo que permite que JavaScript sea tan dinámico y responsivo, a pesar de ser un lenguaje single-threaded.

Recuerda que las micro tasks tienen prioridad sobre las macro tasks, y esto puede ser importante al manejar código asíncrono complejo. Entender estos conceptos es fundamental para convertirte en un experto en JavaScript y crear aplicaciones web increíbles. Sin embargo, ten cuidado al usar queueMicrotask() y no lo hagas en exceso, ya que podría afectar el rendimiento de tu aplicación.

¡Sigue explorando y divirtiéndote con la programación en JavaScript, pero con responsabilidad! JavaScript es como una caja de herramientas mágica, y ahora tienes algunas herramientas más para crear tus próximas obras maestras en la web.

¡Diviértete programando! 🚀🌐

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.