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ícateEjecució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:
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.
Modelo basado en eventos: Cuando estas operaciones delegadas se completan, generan eventos que JavaScript puede manejar.
Callbacks y promesas: Mecanismos para definir qué código debe ejecutarse cuando se completan las operaciones asíncronas.
Este diagrama conceptual ilustra el proceso:
- El código JavaScript se ejecuta en el hilo principal
- Las operaciones asíncronas se delegan al entorno (navegador/Node.js)
- El hilo principal continúa ejecutando otro código
- Cuando una operación asíncrona termina, su callback se coloca en una cola
- 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í:
- Se añade
procesarUsuario()
- Dentro de ella, se añade
saludar()
- Se ejecuta
saludar()
y se elimina del stack - 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:
- Ejecutar el código en el Call Stack hasta que esté vacío
- Comprobar la Microtask Queue y ejecutar todas las microtareas hasta que esta cola esté vacía
- Tomar el primer callback de la Callback Queue y colocarlo en el Call Stack para su ejecución
- Repetir el proceso
Este diagrama conceptual ilustra el flujo:
- El código síncrono se ejecuta en el Call Stack
- Las APIs asíncronas del navegador/Node.js procesan operaciones en segundo plano
- Los callbacks resultantes se colocan en la cola correspondiente
- El Event Loop verifica si el Call Stack está vacío
- Si está vacío, procesa primero todas las microtareas
- 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.
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
Modificación de elementos DOM
Encapsulación
Manipulación DOM
Clases y objetos
Uso de operadores
Uso de operadores
Estructuras de control
Funciones
Excepciones
Transformación con map()
Arrays y Métodos
Transformación con map()
Funciones flecha
Async / Await
Polimorfismo
Variables
Selección de elementos DOM
API Fetch
Encapsulación
Mapas con Map
Creación y uso de variables
Polimorfismo
Tipos de datos
Promises
Estructuras de control
Pruebas unitarias
Encapsulación
Inmutabilidad y programación funcional pura
Destructuring de objetos y arrays
Mapas con Map
Funciones flecha
Polimorfismo
Herencia
Array
Transformación con map()
Gestor de tareas con JavaScript
Manipulación DOM
Funciones
Operadores avanzados
Conjuntos con Set
Funciones flecha
Async / Await
Clases y objetos
Métodos de Strings
Creación y uso de variables
Excepciones
Promises
Funciones cierre (closure)
Funciones cierre (closure)
Herencia
Prototipos y cadena de prototipos
Herencia
Estructuras de control
Selección de elementos DOM
Modificación de elementos DOM
Funciones flecha
Filtrado con filter() y find()
Funciones cierre (closure)
Callbacks
Funciones
Mapas con Map
Reducción con reduce()
Callbacks
Manipulación DOM
Introducción al DOM
Expresiones regulares
Promises
Async / Await
Eventos del DOM
Introducción a JavaScript
Async / Await
Excepciones
Promises
Selección de elementos DOM
Filtrado con filter() y find()
Callbacks
Eventos del DOM
Creación de clases y objetos Restaurante
Reducción con reduce()
Filtrado con filter() y find()
Reducción con reduce()
Conjuntos con Set
Herencia de clases
Eventos del DOM
Clases y objetos
Modificación de elementos DOM
Mapas con Map
Proyecto carrito compra agoodshop
Introducción a JavaScript
Filtrado con filter() y find()
Estructuras de control
Funciones
Reducción con reduce()
Proyecto administrador de contactos
Tipos de datos
Clases y objetos
Array
Conjuntos con Set
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
Introducción Y Entorno
Introducción A Javascript
Introducción Y Entorno
Introducción A Javascript
Introducción Y Entorno
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Métodos De Strings
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Operadores Avanzados
Sintaxis
Funciones
Sintaxis
Expresiones Regulares
Sintaxis
Estructuras De Control
Sintaxis
Variables
Sintaxis
Arrays Y Métodos
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Mapas Con Map
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Funciones Flecha
Programación Funcional
Filtrado Con Filter() Y Find()
Programación Funcional
Transformación Con Map()
Programación Funcional
Reducción Con Reduce()
Programación Funcional
Funciones Flecha
Programación Funcional
Reducción Con Reduce()
Programación Funcional
Filtrado Con Filter() Y Find()
Programación Funcional
Transformación Con Map()
Programación Funcional
Inmutabilidad Y Programación Funcional Pura
Programación Funcional
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
This Y Contexto
Programación Orientada A Objetos
Patrón De Módulos Y Namespace
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Prototipos Y Cadena De Prototipos
Programación Orientada A Objetos
Destructuring De Objetos Y Arrays
Programación Orientada A Objetos
Manipulación Dom
Dom
Selección De Elementos Dom
Dom
Modificación De Elementos Dom
Dom
Eventos Del Dom
Dom
Modificación De Elementos Dom
Dom
Eventos Del Dom
Dom
Localstorage Y Sessionstorage
Dom
Bom (Browser Object Model)
Dom
Modificación De Elementos Dom
Dom
Selección De Elementos Dom
Dom
Callbacks
Programación Asíncrona
Promises
Programación Asíncrona
Async / Await
Programación Asíncrona
Promises
Programación Asíncrona
Async / Await
Programación Asíncrona
Naturaleza De Js Y Event Loop
Programación Asíncrona
Callbacks
Programación Asíncrona
Websockets
Programación Asíncrona
Módulos En Es6
Construcción
Configuración De Bundlers Como Vite
Construcción
Eslint Y Calidad De Código
Construcción
Npm Y Dependencias
Construcción
Introducción A Pruebas En Js
Testing
Pruebas Unitarias
Testing
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