JavaScript

JavaScript

Tutorial JavaScript: Naturaleza de JS y Event Loop

JavaScript: Explora cómo lograr concurrencia con asincronía en un entorno single-thread. Aprende sobre el Event Loop y operaciones no bloqueantes.

Aprende JavaScript y certifícate

Ejecución single-thread y modelo de concurrencia

JavaScript es un lenguaje de programación single-thread, lo que significa que solo puede ejecutar una instrucción a la vez, siguiendo un único hilo de ejecución. Esta característica podría parecer una limitación importante, especialmente cuando necesitamos realizar múltiples operaciones simultáneamente en aplicaciones web modernas. Sin embargo, JavaScript implementa un modelo de concurrencia que le permite manejar múltiples tareas de forma eficiente a pesar de esta restricción.

El único hilo de ejecución

Cuando hablamos de que JavaScript es single-thread, nos referimos específicamente a que:

  • Solo puede ejecutar un fragmento de código a la vez
  • Debe terminar completamente una tarea antes de pasar a la siguiente
  • No puede realizar verdadero paralelismo (múltiples operaciones ejecutándose simultáneamente)

Veamos un ejemplo sencillo que ilustra esta naturaleza secuencial:

console.log("Primero");
console.log("Segundo");
console.log("Tercero");

En este código, JavaScript siempre mostrará los mensajes en el mismo orden, uno tras otro, porque sigue estrictamente la secuencia de instrucciones en su único hilo.

El problema del bloqueo

La naturaleza single-thread presenta un desafío importante: las operaciones que toman mucho tiempo pueden bloquear todo el programa. Consideremos este ejemplo:

console.log("Inicio");
// Operación que simula un proceso intensivo
for (let i = 0; i < 10000000000; i++) {
  // Proceso intensivo que bloquea el hilo principal
}
console.log("Fin");

Durante la ejecución del bucle, JavaScript no puede hacer nada más. Si este código se ejecutara en un navegador, la interfaz de usuario se congelaría completamente hasta que el bucle termine, creando una experiencia de usuario deficiente.

Concurrencia mediante asincronía

Para superar esta limitación, JavaScript implementa un modelo de concurrencia basado en eventos. Este modelo permite que el código responda a eventos y maneje operaciones asíncronas sin bloquear el hilo principal. Los componentes clave de este modelo son:

  • APIs asíncronas: Proporcionadas por el entorno de ejecución (navegador o Node.js)
  • Callbacks: Funciones que se ejecutarán cuando una operación asíncrona se complete
  • Event Loop: Mecanismo que coordina la ejecución de código asíncrono

Veamos cómo funciona con un ejemplo:

console.log("Inicio");

setTimeout(() => {
  console.log("Operación asíncrona completada");
}, 2000);

console.log("Fin");

La salida de este código será:

Inicio
Fin
Operación asíncrona completada

Esto demuestra cómo JavaScript puede continuar ejecutando código mientras una operación asíncrona (en este caso, un temporizador) se procesa en segundo plano.

Cómo JavaScript logra la concurrencia

JavaScript logra la concurrencia a través de un sistema ingenioso que combina:

  1. Delegación de tareas: JavaScript delega operaciones intensivas o de larga duración (como solicitudes de red, operaciones de archivo o temporizadores) a APIs del entorno de ejecución.

  2. Modelo basado en eventos: Cuando estas operaciones delegadas se completan, generan eventos que JavaScript puede manejar.

  3. Callbacks y promesas: Mecanismos para definir qué código debe ejecutarse cuando se completan las operaciones asíncronas.

Este diagrama conceptual ilustra el proceso:

  1. El código JavaScript se ejecuta en el hilo principal
  2. Las operaciones asíncronas se delegan al entorno (navegador/Node.js)
  3. El hilo principal continúa ejecutando otro código
  4. Cuando una operación asíncrona termina, su callback se coloca en una cola
  5. El Event Loop verifica si el hilo principal está libre y, en ese caso, toma el siguiente callback de la cola y lo ejecuta

Ejemplo práctico de concurrencia

Veamos un ejemplo más completo que muestra cómo JavaScript maneja múltiples operaciones asíncronas:

console.log("🚀 Iniciando aplicación");

// Simulación de una solicitud de red
setTimeout(() => {
  console.log("📊 Datos recibidos del servidor");
}, 2000);

// Simulación de carga de imagen
setTimeout(() => {
  console.log("🖼️ Imagen cargada");
}, 1000);

// Código que se ejecuta inmediatamente
for (let i = 0; i < 3; i++) {
  console.log(`⚙️ Procesando tarea ${i + 1}`);
}

console.log("✅ Interfaz lista");

La salida será:

🚀 Iniciando aplicación
⚙️ Procesando tarea 1
⚙️ Procesando tarea 2
⚙️ Procesando tarea 3
✅ Interfaz lista
🖼️ Imagen cargada
📊 Datos recibidos del servidor

Este ejemplo muestra cómo JavaScript puede:

  • Iniciar múltiples operaciones asíncronas (las dos llamadas a setTimeout)
  • Continuar ejecutando código sincrónico (el bucle for)
  • Completar las operaciones asíncronas en el orden en que terminan, no en el orden en que se iniciaron

Concurrencia vs. Paralelismo

Es importante distinguir entre estos dos conceptos:

  • Concurrencia: Manejar múltiples tareas progresando al mismo tiempo, pero no necesariamente ejecutándose simultáneamente. JavaScript implementa concurrencia.
  • Paralelismo: Ejecutar múltiples tareas exactamente al mismo tiempo en diferentes hilos o procesadores. JavaScript no implementa paralelismo nativo.

JavaScript logra la ilusión de paralelismo a través de su modelo de concurrencia, permitiendo que las aplicaciones sean responsivas mientras realizan múltiples operaciones.

Web Workers: Ampliando el modelo

Para tareas intensivas de CPU que podrían bloquear el hilo principal, JavaScript ofrece Web Workers, que permiten ejecutar código en hilos separados:

// main.js
console.log("Hilo principal: Iniciando worker");

const worker = new Worker('worker.js');

worker.onmessage = function(event) {
  console.log(`Hilo principal: Recibido resultado ${event.data}`);
};

console.log("Hilo principal: Continúa ejecutando código");
// worker.js
console.log("Worker: Iniciando cálculo intensivo");

// Simulación de operación intensiva
let result = 0;
for (let i = 0; i < 1000000000; i++) {
  result += i;
}

postMessage(result);

Los Web Workers complementan el modelo de concurrencia de JavaScript, permitiendo operaciones intensivas sin bloquear la interfaz de usuario, aunque siguen teniendo limitaciones en comparación con los sistemas de multihilo completos de otros lenguajes.

Anatomía del Event Loop: Call stack, callback queue y microtask queue en el flujo de ejecución

El Event Loop es el mecanismo central que permite a JavaScript ejecutar código asíncrono mientras mantiene su naturaleza single-thread. Para entender completamente cómo funciona, necesitamos examinar sus componentes principales y cómo interactúan entre sí.

El Call Stack

El Call Stack (pila de llamadas) es una estructura de datos que registra dónde estamos en la ejecución del programa. Funciona siguiendo el principio LIFO (Last In, First Out):

  • Cuando se invoca una función, se coloca (push) en la parte superior del stack
  • Cuando una función termina, se elimina (pop) del stack
  • JavaScript solo puede ejecutar el código que está en la parte superior del stack
function saludar() {
  console.log("Hola");
}

function procesarUsuario() {
  saludar();
  console.log("Procesando datos");
}

procesarUsuario();

En este ejemplo, el Call Stack evoluciona así:

  1. Se añade procesarUsuario()
  2. Dentro de ella, se añade saludar()
  3. Se ejecuta saludar() y se elimina del stack
  4. Se continúa con procesarUsuario() y finalmente se elimina

La Callback Queue

La Callback Queue (cola de callbacks) es donde se almacenan las funciones callback que están listas para ejecutarse. Estas funciones provienen principalmente de:

  • Eventos de usuario (clicks, teclas)
  • Temporizadores (setTimeout, setInterval)
  • Solicitudes de red (XHR, fetch)
  • Otras APIs asíncronas del navegador o Node.js

A diferencia del Call Stack, la Callback Queue sigue el principio FIFO (First In, First Out): el primer callback que entra es el primero en ser procesado.

console.log("Inicio");

setTimeout(() => {
  console.log("Callback del temporizador");
}, 0);

console.log("Fin");

Aunque especificamos un tiempo de espera de 0ms, la salida será:

Inicio
Fin
Callback del temporizador

Esto ocurre porque el callback debe esperar en la cola hasta que el Call Stack esté vacío.

La Microtask Queue

La Microtask Queue (cola de microtareas) es similar a la Callback Queue, pero con una prioridad más alta. Las microtareas incluyen:

  • Callbacks de Promesas (.then(), .catch(), .finally())
  • Operaciones de queueMicrotask()
  • Algunas partes de las APIs de DOM/HTML

Lo crucial es que todas las microtareas se procesan inmediatamente después de que se vacía el Call Stack y antes de procesar cualquier callback de la Callback Queue.

console.log("Inicio");

// Callback regular (macrotarea)
setTimeout(() => {
  console.log("Timeout (macrotarea)");
}, 0);

// Microtarea
Promise.resolve().then(() => {
  console.log("Promesa (microtarea)");
});

console.log("Fin");

La salida será:

Inicio
Fin
Promesa (microtarea)
Timeout (macrotarea)

Esto demuestra cómo las microtareas tienen prioridad sobre las tareas regulares (macrotareas).

El funcionamiento del Event Loop

El Event Loop es el mecanismo que coordina todo este proceso. Su funcionamiento puede resumirse en estos pasos:

  1. Ejecutar el código en el Call Stack hasta que esté vacío
  2. Comprobar la Microtask Queue y ejecutar todas las microtareas hasta que esta cola esté vacía
  3. Tomar el primer callback de la Callback Queue y colocarlo en el Call Stack para su ejecución
  4. Repetir el proceso

Este diagrama conceptual ilustra el flujo:

  1. El código síncrono se ejecuta en el Call Stack
  2. Las APIs asíncronas del navegador/Node.js procesan operaciones en segundo plano
  3. Los callbacks resultantes se colocan en la cola correspondiente
  4. El Event Loop verifica si el Call Stack está vacío
  5. Si está vacío, procesa primero todas las microtareas
  6. Luego toma un callback de la Callback Queue y lo coloca en el Call Stack

Ejemplo completo del Event Loop

Veamos un ejemplo que ilustra la interacción entre todos estos componentes:

console.log("1. Script inicia");

setTimeout(() => {
  console.log("2. Timeout callback (macrotarea)");
  
  Promise.resolve().then(() => {
    console.log("3. Promesa dentro del timeout (microtarea)");
  });
}, 0);

Promise.resolve().then(() => {
  console.log("4. Primera promesa (microtarea)");
  
  setTimeout(() => {
    console.log("5. Timeout dentro de promesa (macrotarea)");
  }, 0);
});

Promise.resolve().then(() => {
  console.log("6. Segunda promesa (microtarea)");
});

console.log("7. Script termina");

La salida será:

1. Script inicia
7. Script termina
4. Primera promesa (microtarea)
6. Segunda promesa (microtarea)
2. Timeout callback (macrotarea)
3. Promesa dentro del timeout (microtarea)
5. Timeout dentro de promesa (macrotarea)

Este resultado demuestra claramente:

  • El código síncrono se ejecuta primero (1, 7)
  • Después se procesan todas las microtareas pendientes (4, 6)
  • Luego se procesa una macrotarea (2)
  • Inmediatamente después se procesan las microtareas generadas (3)
  • Finalmente se procesa la siguiente macrotarea (5)

Visualización del Event Loop

Para entender mejor el Event Loop, podemos usar herramientas de visualización o crear ejemplos que muestren su comportamiento:

// Función para visualizar el orden de ejecución
function visualizarEventLoop() {
  console.log("🔄 Call Stack: Función principal ejecutándose");
  
  // Tarea 1: setTimeout (macrotarea)
  setTimeout(() => {
    console.log("⏱️ Callback Queue → Call Stack: setTimeout ejecutándose");
  }, 0);
  
  // Tarea 2: Promise (microtarea)
  Promise.resolve().then(() => {
    console.log("🔍 Microtask Queue → Call Stack: Promise ejecutándose");
    
    // Anidamos otra microtarea
    Promise.resolve().then(() => {
      console.log("🔍 Microtask Queue → Call Stack: Promise anidada");
    });
  });
  
  // Tarea 3: setTimeout más largo (macrotarea)
  setTimeout(() => {
    console.log("⏱️ Callback Queue → Call Stack: segundo setTimeout");
  }, 0);
  
  console.log("🔄 Call Stack: Función principal terminando");
}

visualizarEventLoop();

La salida mostrará claramente el orden de procesamiento y cómo el Event Loop prioriza las diferentes tareas.

Rendimiento y consideraciones prácticas

Entender la anatomía del Event Loop tiene implicaciones prácticas importantes:

  • Bloqueo del hilo principal: Las operaciones largas en el Call Stack bloquean todo el proceso, incluyendo la interfaz de usuario.

  • Priorización de microtareas: Las microtareas pueden retrasar indefinidamente las macrotareas si continuamente generan nuevas microtareas.

// Ejemplo de microtareas que bloquean macrotareas
setTimeout(() => console.log("Esta macrotarea se retrasa"), 0);

function generarMicrotareasInfinitas(i = 0) {
  if (i < 5) { // Limitamos a 5 para el ejemplo
    console.log(`Microtarea #${i}`);
    Promise.resolve().then(() => generarMicrotareasInfinitas(i + 1));
  }
}

generarMicrotareasInfinitas();
  • Optimización de rendimiento: Dividir tareas grandes en fragmentos más pequeños permite que el Event Loop intercale otras operaciones, mejorando la capacidad de respuesta.
// En lugar de procesar 1 millón de elementos de una vez
function procesarLote(datos, inicio, tamañoLote, callback) {
  setTimeout(() => {
    const fin = Math.min(inicio + tamañoLote, datos.length);
    
    // Procesar un lote pequeño
    for (let i = inicio; i < fin; i++) {
      // Procesar datos[i]
    }
    
    // Si quedan elementos, programar el siguiente lote
    if (fin < datos.length) {
      procesarLote(datos, fin, tamañoLote, callback);
    } else {
      callback();
    }
  }, 0);
}

// Uso
const granArray = new Array(1000000).fill(0);
procesarLote(granArray, 0, 10000, () => console.log("Procesamiento completo"));

Comprender la anatomía del Event Loop es fundamental para escribir código JavaScript eficiente y responsivo, especialmente en aplicaciones que manejan múltiples operaciones asíncronas simultáneamente.

Operaciones bloqueantes vs. no bloqueantes: Impacto en la experiencia de usuario y rendimiento

En el núcleo del modelo de ejecución de JavaScript, la distinción entre operaciones bloqueantes y no bloqueantes es fundamental para desarrollar aplicaciones web eficientes y con buena experiencia de usuario. Esta diferencia determina cómo nuestro código interactúa con el único hilo de ejecución disponible y afecta directamente al rendimiento percibido por los usuarios.

Operaciones bloqueantes

Las operaciones bloqueantes son aquellas que ocupan completamente el hilo principal de JavaScript, impidiendo que cualquier otro código se ejecute hasta que finalicen. Durante este tiempo, la interfaz de usuario queda congelada, sin poder responder a interacciones del usuario.

function operacionBloqueante() {
  console.log("Iniciando operación bloqueante...");
  
  // Simulación de operación intensiva
  const inicio = Date.now();
  while (Date.now() - inicio < 3000) {
    // Bucle intensivo que bloquea el hilo por 3 segundos
  }
  
  console.log("Operación bloqueante completada");
}

console.log("Antes de la operación");
operacionBloqueante();
console.log("Después de la operación");

En este ejemplo, la interfaz de usuario quedará completamente bloqueada durante 3 segundos. El usuario no podrá hacer clic en botones, escribir en campos de texto o realizar ninguna interacción hasta que termine la operación.

Operaciones no bloqueantes

Las operaciones no bloqueantes, por el contrario, liberan el hilo principal mientras se procesan, permitiendo que la aplicación siga respondiendo a eventos y ejecutando otro código. Estas operaciones utilizan las APIs asíncronas proporcionadas por el entorno de ejecución.

function operacionNoBloqueante() {
  console.log("Iniciando operación no bloqueante...");
  
  setTimeout(() => {
    // Código que se ejecutará después, sin bloquear el hilo principal
    console.log("Operación no bloqueante completada");
  }, 3000);
}

console.log("Antes de la operación");
operacionNoBloqueante();
console.log("Después de la operación");

En este caso, la interfaz permanece completamente funcional durante los 3 segundos que tarda la operación, ya que el temporizador se procesa fuera del hilo principal.

Impacto en la experiencia de usuario

El uso de operaciones bloqueantes o no bloqueantes tiene un impacto directo en cómo los usuarios perciben nuestra aplicación:

  • Con operaciones bloqueantes:
  • La interfaz se congela temporalmente
  • Los elementos visuales no responden a interacciones
  • El navegador puede mostrar advertencias de "script no responde"
  • Los usuarios perciben la aplicación como lenta o defectuosa
  • Con operaciones no bloqueantes:
  • La interfaz permanece receptiva
  • Los usuarios pueden seguir interactuando con la aplicación
  • Se pueden mostrar indicadores de progreso
  • La aplicación se percibe como rápida y fluida

Identificación de operaciones potencialmente bloqueantes

Algunas operaciones comunes que pueden bloquear el hilo principal incluyen:

  • Cálculos intensivos: Algoritmos complejos, procesamiento de grandes conjuntos de datos
  • Manipulación DOM masiva: Actualizar muchos elementos del DOM en un solo ciclo
  • Parseo de archivos grandes: JSON, XML o texto sin procesar
  • Sincronización forzada del layout: Leer propiedades que requieren recálculo del layout
// Ejemplo de manipulación DOM bloqueante
function agregarMuchasFilas() {
  const tabla = document.getElementById("miTabla");
  
  console.time("operación DOM");
  for (let i = 0; i < 10000; i++) {
    const fila = document.createElement("tr");
    const celda = document.createElement("td");
    celda.textContent = `Fila ${i}`;
    fila.appendChild(celda);
    tabla.appendChild(fila); // Cada inserción fuerza un recálculo
  }
  console.timeEnd("operación DOM");
}

Transformando operaciones bloqueantes en no bloqueantes

Existen varias estrategias para convertir operaciones bloqueantes en no bloqueantes:

  • 1. División en fragmentos más pequeños:
function procesarDatosPorLotes(datos, tamañoLote = 1000) {
  let indice = 0;
  
  function procesarLote() {
    const limite = Math.min(indice + tamañoLote, datos.length);
    
    // Procesar un lote pequeño
    for (let i = indice; i < limite; i++) {
      // Procesar datos[i]
    }
    
    indice = limite;
    
    // Si quedan datos, programar el siguiente lote
    if (indice < datos.length) {
      setTimeout(procesarLote, 0);
    } else {
      console.log("Procesamiento completo");
    }
  }
  
  procesarLote();
}
  • 2. Uso de Web Workers para cálculos intensivos:
// En el script principal
const worker = new Worker('calculador.js');

worker.onmessage = function(event) {
  console.log("Resultado recibido:", event.data);
  document.getElementById("resultado").textContent = event.data;
};

// Enviar datos al worker
worker.postMessage({
  numeros: Array.from({length: 10000000}, (_, i) => i),
  operacion: 'suma'
});

// En calculador.js (archivo separado)
self.onmessage = function(event) {
  const { numeros, operacion } = event.data;
  let resultado;
  
  if (operacion === 'suma') {
    resultado = numeros.reduce((sum, num) => sum + num, 0);
  }
  
  self.postMessage(resultado);
};
  • 3. Optimización de manipulaciones DOM:
// Versión optimizada del ejemplo anterior
function agregarMuchasFilasOptimizado() {
  const tabla = document.getElementById("miTabla");
  const fragmento = document.createDocumentFragment();
  
  console.time("operación DOM optimizada");
  for (let i = 0; i < 10000; i++) {
    const fila = document.createElement("tr");
    const celda = document.createElement("td");
    celda.textContent = `Fila ${i}`;
    fila.appendChild(celda);
    fragmento.appendChild(fila); // No causa recálculos
  }
  
  tabla.appendChild(fragmento); // Una sola operación de DOM
  console.timeEnd("operación DOM optimizada");
}

Medición del impacto en el rendimiento

Para evaluar objetivamente el impacto de las operaciones bloqueantes vs. no bloqueantes, podemos utilizar herramientas de desarrollo:

  • Performance panel en Chrome DevTools
  • Lighthouse para auditorías de rendimiento
  • First Input Delay (FID) como métrica clave
  • Time to Interactive (TTI) para medir cuándo la página es interactiva
// Ejemplo de código para medir el tiempo de bloqueo
let tiempoBloqueoTotal = 0;
let ultimoTiempo = performance.now();

// Ejecutar periódicamente para detectar bloqueos
const intervalo = setInterval(() => {
  const ahora = performance.now();
  const delta = ahora - ultimoTiempo;
  
  // Si pasaron más de 50ms, consideramos que hubo bloqueo
  if (delta > 50) {
    tiempoBloqueoTotal += delta;
    console.warn(`Bloqueo detectado: ${delta.toFixed(2)}ms`);
  }
  
  ultimoTiempo = ahora;
}, 20);

// Detener la medición después de un tiempo
setTimeout(() => {
  clearInterval(intervalo);
  console.log(`Tiempo total de bloqueo: ${tiempoBloqueoTotal.toFixed(2)}ms`);
}, 10000);

Patrones para mantener la responsividad

Algunos patrones efectivos para mantener la aplicación responsiva incluyen:

  • Debouncing y throttling para limitar la frecuencia de ejecución de funciones
// Implementación de debounce
function debounce(func, wait) {
  let timeout;
  
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

// Uso con un evento de scroll
const actualizarPosicion = debounce(() => {
  // Código que actualiza la posición
  console.log("Posición actualizada");
}, 200);

window.addEventListener('scroll', actualizarPosicion);
  • Indicadores visuales de progreso para operaciones largas
async function cargarDatos() {
  // Mostrar indicador de carga
  document.getElementById("loader").style.display = "block";
  
  try {
    // Operación asíncrona
    const respuesta = await fetch('/api/datos');
    const datos = await respuesta.json();
    
    // Procesar datos
    return datos;
  } finally {
    // Ocultar indicador de carga cuando termine
    document.getElementById("loader").style.display = "none";
  }
}
  • Priorización de tareas críticas para la experiencia de usuario
// Priorizar la renderización inicial
document.addEventListener('DOMContentLoaded', () => {
  // 1. Renderizar contenido crítico inmediatamente
  renderizarContenidoCritico();
  
  // 2. Programar contenido no crítico para después
  setTimeout(() => {
    cargarYRenderizarContenidoSecundario();
  }, 100);
  
  // 3. Cargar datos en segundo plano con baja prioridad
  requestIdleCallback(() => {
    precargarDatosAdicionales();
  });
});

Consideraciones para diferentes entornos

El impacto de las operaciones bloqueantes varía según el entorno:

  • Navegadores: El bloqueo afecta directamente la interfaz de usuario
  • Node.js: El bloqueo afecta la capacidad de manejar múltiples solicitudes
  • Aplicaciones móviles: El bloqueo puede ser más severo debido a limitaciones de hardware

En todos los casos, la clave es identificar las operaciones potencialmente bloqueantes y aplicar las técnicas adecuadas para convertirlas en no bloqueantes, manteniendo así la fluidez y responsividad de la aplicación.

La elección entre operaciones bloqueantes y no bloqueantes no es solo una cuestión técnica, sino que tiene un impacto directo en cómo los usuarios perciben y experimentan nuestras aplicaciones. Un enfoque consciente hacia la programación no bloqueante es esencial para crear experiencias web modernas y de alta calidad.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

Plan mensual

19.00 € /mes

Precio normal mensual: 19 €
47 % DE DESCUENTO

Plan anual

10.00 € /mes

Ahorras 108 € al año
Precio normal anual: 120 €
Aprende JavaScript online

Ejercicios de esta lección Naturaleza de JS y Event Loop

Evalúa tus conocimientos de esta lección Naturaleza de JS y Event Loop con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Array

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Proyecto

Encapsulación

JavaScript
Puzzle

Manipulación DOM

JavaScript
Proyecto

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Funciones

JavaScript
Código

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

JavaScript
Código

Polimorfismo

JavaScript
Código

Variables

JavaScript
Código

Selección de elementos DOM

JavaScript
Puzzle

API Fetch

JavaScript
Código

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Tipos de datos

JavaScript
Puzzle

Promises

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Encapsulación

JavaScript
Código

Inmutabilidad y programación funcional pura

JavaScript
Código

Destructuring de objetos y arrays

JavaScript
Código

Mapas con Map

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Herencia

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Gestor de tareas con JavaScript

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Operadores avanzados

JavaScript
Código

Conjuntos con Set

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Clases y objetos

JavaScript
Código

Métodos de Strings

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Funciones cierre (closure)

JavaScript
Código

Herencia

JavaScript
Puzzle

Prototipos y cadena de prototipos

JavaScript
Código

Herencia

JavaScript
Test

Estructuras de control

JavaScript
Código

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Funciones flecha

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Callbacks

JavaScript
Código

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Introducción al DOM

JavaScript
Proyecto

Expresiones regulares

JavaScript
Código

Promises

JavaScript
Test

Async / Await

JavaScript
Test

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Excepciones

JavaScript
Código

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

Eventos del DOM

JavaScript
Proyecto

Creación de clases y objetos Restaurante

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Puzzle

Reducción con reduce()

JavaScript
Puzzle

Conjuntos con Set

JavaScript
Puzzle

Herencia de clases

JavaScript
Código

Eventos del DOM

JavaScript
Test

Clases y objetos

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Puzzle

Mapas con Map

JavaScript
Puzzle

Proyecto carrito compra agoodshop

JavaScript
Proyecto

Introducción a JavaScript

JavaScript
Test

Filtrado con filter() y find()

JavaScript
Código

Estructuras de control

JavaScript
Código

Funciones

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Todas las lecciones de JavaScript

Accede a todas las lecciones de JavaScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Javascript

JavaScript

Introducción Y Entorno

Introducción A Javascript

JavaScript

Introducción Y Entorno

Introducción A Javascript

JavaScript

Introducción Y Entorno

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Métodos De Strings

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Operadores Avanzados

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Expresiones Regulares

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Arrays Y Métodos

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Funciones Flecha

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Inmutabilidad Y Programación Funcional Pura

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

JavaScript

Programación Orientada A Objetos

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Prototipos Y Cadena De Prototipos

JavaScript

Programación Orientada A Objetos

Destructuring De Objetos Y Arrays

JavaScript

Programación Orientada A Objetos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Naturaleza De Js Y Event Loop

JavaScript

Programación Asíncrona

Callbacks

JavaScript

Programación Asíncrona

Websockets

JavaScript

Programación Asíncrona

Módulos En Es6

JavaScript

Construcción

Configuración De Bundlers Como Vite

JavaScript

Construcción

Eslint Y Calidad De Código

JavaScript

Construcción

Npm Y Dependencias

JavaScript

Construcción

Introducción A Pruebas En Js

JavaScript

Testing

Pruebas Unitarias

JavaScript

Testing

Accede GRATIS a JavaScript y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Entender el concepto de ejecución single-thread en JavaScript
  • Aprender el modelo de concurrencia basado en eventos
  • Comprender el funcionamiento del Event Loop
  • Diferenciar entre operaciones bloqueantes y no bloqueantes
  • Implementar Web Workers para tareas intensivas
  • Aplicar técnicas de asincronía: callbacks, promesas, async/await