JavaScript

JavaScript

Tutorial JavaScript: Callbacks

JavaScript y uso de funciones de orden superior para asincronía. Aprende a manejar callbacks y mejorar flujo de control en programación asíncrona.

Aprende JavaScript y certifícate

Funciones de orden superior: Fundamentos de la programación funcional aplicados a la asincronía

Las funciones de orden superior son uno de los pilares fundamentales de la programación funcional en JavaScript. Se trata de funciones que pueden recibir otras funciones como argumentos o devolverlas como resultado. Este concepto es crucial para entender cómo JavaScript maneja la asincronía mediante callbacks.

En JavaScript, las funciones son ciudadanos de primera clase, lo que significa que pueden ser tratadas como cualquier otro valor: asignadas a variables, pasadas como argumentos o devueltas por otras funciones. Esta característica es la que permite implementar patrones asíncronos mediante callbacks.

Funciones como valores

Antes de profundizar en la asincronía, es importante entender cómo JavaScript trata las funciones como valores:

// Asignar una función a una variable
const saludar = function(nombre) {
  return `Hola, ${nombre}!`;
};

// Pasar una función como argumento
function ejecutarFuncion(fn, valor) {
  return fn(valor);
}

const resultado = ejecutarFuncion(saludar, "María");
console.log(resultado); // "Hola, María!"

Esta capacidad de manipular funciones como valores es lo que nos permite implementar callbacks para operaciones asíncronas.

Funciones que devuelven funciones

Otro concepto clave es la capacidad de crear funciones que devuelven otras funciones:

function multiplicadorPor(factor) {
  // Devuelve una nueva función
  return function(numero) {
    return numero * factor;
  };
}

const duplicar = multiplicadorPor(2);
const triplicar = multiplicadorPor(3);

console.log(duplicar(5));  // 10
console.log(triplicar(5)); // 15

Este patrón es especialmente útil para crear funciones especializadas que pueden utilizarse en contextos asíncronos.

Aplicando funciones de orden superior a la asincronía

En JavaScript, las operaciones asíncronas como lecturas de archivos, peticiones de red o temporizadores utilizan callbacks para manejar resultados que no están disponibles inmediatamente. Veamos cómo las funciones de orden superior facilitan este patrón:

function realizarOperacionAsincrona(datos, callback) {
  // Simulamos una operación asíncrona con setTimeout
  setTimeout(() => {
    const resultado = datos.toUpperCase();
    callback(resultado);
  }, 1000);
}

realizarOperacionAsincrona("datos procesados", resultado => {
  console.log(resultado); // "DATOS PROCESADOS"
});

En este ejemplo, realizarOperacionAsincrona es una función de orden superior que acepta datos y una función callback. La operación se ejecuta de forma asíncrona (simulada con setTimeout), y cuando finaliza, invoca al callback con el resultado.

Composición de operaciones asíncronas

Las funciones de orden superior nos permiten componer operaciones asíncronas, encadenando callbacks para realizar secuencias de operaciones:

function obtenerDatos(id, callback) {
  setTimeout(() => {
    const datos = { id, nombre: "Producto ejemplo" };
    callback(datos);
  }, 1000);
}

function procesarDatos(datos, callback) {
  setTimeout(() => {
    const procesado = { ...datos, procesado: true };
    callback(procesado);
  }, 1000);
}

// Composición de operaciones asíncronas
obtenerDatos(123, datos => {
  console.log("Datos obtenidos:", datos);
  procesarDatos(datos, resultado => {
    console.log("Datos procesados:", resultado);
  });
});

Esta capacidad de composición es fundamental, aunque como veremos en secciones posteriores, puede llevar a problemas de legibilidad cuando se anidan demasiados callbacks.

Transformación de operaciones síncronas en asíncronas

Las funciones de orden superior también nos permiten transformar operaciones síncronas en asíncronas:

function hacerAsincrono(fn) {
  return function(...args) {
    const callback = args.pop();
    setTimeout(() => {
      try {
        const resultado = fn(...args);
        callback(null, resultado);
      } catch (error) {
        callback(error);
      }
    }, 0);
  };
}

// Función síncrona
function sumar(a, b) {
  return a + b;
}

// Versión asíncrona
const sumarAsincrono = hacerAsincrono(sumar);

sumarAsincrono(5, 3, (error, resultado) => {
  if (error) {
    console.error("Error:", error);
    return;
  }
  console.log("Resultado:", resultado); // 8
});

En este ejemplo, hacerAsincrono es una función de orden superior que transforma cualquier función síncrona en una versión asíncrona que utiliza callbacks.

Funciones de orden superior para control de flujo asíncrono

Podemos crear funciones de orden superior que nos ayuden a controlar el flujo de operaciones asíncronas:

function serie(tareas, finalCallback) {
  const resultados = [];
  let indiceActual = 0;

  function siguienteTarea() {
    if (indiceActual === tareas.length) {
      finalCallback(null, resultados);
      return;
    }

    tareas[indiceActual]((error, resultado) => {
      if (error) {
        finalCallback(error);
        return;
      }
      
      resultados.push(resultado);
      indiceActual++;
      siguienteTarea();
    });
  }

  siguienteTarea();
}

// Ejemplo de uso
const tarea1 = callback => setTimeout(() => callback(null, "Resultado 1"), 1000);
const tarea2 = callback => setTimeout(() => callback(null, "Resultado 2"), 500);

serie([tarea1, tarea2], (error, resultados) => {
  if (error) {
    console.error("Error:", error);
    return;
  }
  console.log("Resultados:", resultados); // ["Resultado 1", "Resultado 2"]
});

Esta función serie ejecuta una lista de tareas asíncronas en secuencia, recopilando sus resultados. Es un ejemplo de cómo las funciones de orden superior pueden ayudarnos a abstraer patrones complejos de control de flujo asíncrono.

Closures en callbacks asíncronos

Los closures (cierres) son fundamentales para mantener el contexto en operaciones asíncronas. Un closure permite a una función acceder a variables de su ámbito exterior incluso después de que la función externa haya terminado de ejecutarse:

function crearContadorAsincrono() {
  let contador = 0;
  
  return function incrementar(callback) {
    setTimeout(() => {
      contador++;
      callback(contador);
    }, 1000);
  };
}

const incrementarContador = crearContadorAsincrono();

incrementarContador(valor => console.log(valor)); // 1
incrementarContador(valor => console.log(valor)); // 2
incrementarContador(valor => console.log(valor)); // 3

En este ejemplo, la función incrementar mantiene acceso a la variable contador gracias al closure, incluso cuando se ejecuta de forma asíncrona.

Las funciones de orden superior, combinadas con los conceptos de programación funcional como closures y funciones como valores, proporcionan la base para implementar patrones asíncronos en JavaScript. Estos fundamentos son esenciales para entender no solo los callbacks, sino también las abstracciones más modernas como Promises y async/await que veremos en lecciones posteriores.

Patrones de implementación: Error-first callbacks y convenciones en APIs de Node.js

En el ecosistema de Node.js, los callbacks no se implementan de cualquier manera, sino que siguen patrones y convenciones específicas que facilitan su uso consistente a través de diferentes APIs. El patrón más importante y ampliamente adoptado es el error-first callback (callback con error primero), también conocido como "Node-style callback".

El patrón Error-First Callback

Este patrón establece una convención simple pero poderosa: el primer parámetro de cualquier función callback siempre debe ser un objeto de error (o null si no hay error), seguido de los resultados de la operación.

function operacionAsincrona(parametros, callback) {
  // Realizar operación...
  if (/* ocurrió un error */) {
    callback(new Error('Descripción del error'));
    return;
  }
  
  // Operación exitosa
  callback(null, resultado);
}

Esta convención ofrece varias ventajas clave:

  • Proporciona una forma consistente de manejar errores
  • Permite detección temprana de problemas
  • Facilita la propagación de errores en operaciones encadenadas
  • Establece un contrato claro entre las APIs y sus consumidores

Implementación en APIs nativas de Node.js

Las APIs del núcleo de Node.js siguen rigurosamente este patrón. Veamos algunos ejemplos:

Sistema de archivos (fs)

const fs = require('fs');

fs.readFile('archivo.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Error al leer el archivo:', err);
    return;
  }
  
  console.log('Contenido del archivo:', data);
});

Operaciones de red

const http = require('http');

http.get('http://ejemplo.com', (res) => {
  let data = '';
  
  res.on('data', (chunk) => {
    data += chunk;
  });
  
  res.on('end', () => {
    console.log('Respuesta completa:', data);
  });
}).on('error', (err) => {
  console.error('Error en la petición:', err);
});

Buenas prácticas al implementar error-first callbacks

Cuando diseñes tus propias funciones asíncronas, es recomendable seguir estas prácticas establecidas:

  • Consistencia en la firma: Mantén el parámetro de error siempre como primer argumento.
// ✅ Correcto
function buscarUsuario(id, callback) {
  // Implementación...
  callback(null, usuario);
}

// ❌ Incorrecto
function buscarUsuario(id, callback) {
  // Implementación...
  callback(usuario, null); // Error como segundo parámetro
}
  • Manejo explícito de errores: Siempre verifica el error antes de procesar resultados.
buscarUsuario(123, (err, usuario) => {
  if (err) {
    // Manejar el error primero
    console.error('Error:', err);
    return;
  }
  
  // Procesar el resultado solo si no hay error
  console.log('Usuario encontrado:', usuario);
});
  • Errores informativos: Usa objetos Error con mensajes descriptivos.
function validarDatos(datos, callback) {
  if (!datos.nombre) {
    callback(new Error('El nombre es obligatorio'));
    return;
  }
  
  // Continuar con la validación...
  callback(null, true);
}
  • Retorno temprano: Después de invocar el callback con un error, usa return para evitar ejecución adicional.
function procesarPago(monto, callback) {
  if (monto <= 0) {
    callback(new Error('El monto debe ser positivo'));
    return; // Importante: evita ejecución adicional
  }
  
  // Continuar con el procesamiento...
}

Tipos de errores en Node.js

Node.js proporciona varias clases de error que puedes utilizar para ser más específico:

// Error genérico
callback(new Error('Mensaje de error'));

// Error específico para operaciones de red
callback(new TypeError('El parámetro debe ser una cadena'));

// Error personalizado
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

callback(new ValidationError('Datos inválidos'));

Propagación de errores en cadenas de callbacks

Una ventaja del patrón error-first es la facilidad para propagar errores a través de múltiples operaciones asíncronas:

function procesarArchivo(ruta, callback) {
  fs.readFile(ruta, 'utf8', (err, contenido) => {
    if (err) {
      callback(err); // Propaga el error hacia arriba
      return;
    }
    
    try {
      const datos = JSON.parse(contenido);
      procesarDatos(datos, callback); // Pasa el mismo callback
    } catch (error) {
      callback(new Error(`Error al parsear JSON: ${error.message}`));
    }
  });
}

function procesarDatos(datos, callback) {
  // Procesamiento...
  if (/* error durante procesamiento */) {
    callback(new Error('Error en procesamiento'));
    return;
  }
  
  callback(null, resultadoFinal);
}

Convenciones adicionales en APIs de Node.js

Además del patrón error-first, las APIs de Node.js siguen otras convenciones importantes:

  • Opciones como objeto: Muchas funciones aceptan un objeto de opciones para configuración.
fs.readFile('archivo.txt', {
  encoding: 'utf8',
  flag: 'r'
}, (err, data) => {
  // Manejo de resultado
});
  • Callbacks opcionales: Algunas APIs permiten omitir el callback, devolviendo una Promise en su lugar (en versiones recientes).
// Con callback
fs.readFile('archivo.txt', (err, data) => {
  // Manejo de resultado
});

// Con promesas (Node.js moderno)
const { promises: fsPromises } = require('fs');
fsPromises.readFile('archivo.txt')
  .then(data => console.log(data))
  .catch(err => console.error(err));
  • Métodos síncronos con sufijo 'Sync': Para operaciones que tienen versiones síncronas y asíncronas.
// Versión asíncrona con callback
fs.readFile('archivo.txt', (err, data) => {
  // Manejo asíncrono
});

// Versión síncrona (bloqueante)
try {
  const data = fs.readFileSync('archivo.txt');
  console.log(data);
} catch (err) {
  console.error(err);
}

Implementación de funciones que siguen el patrón error-first

Cuando crees tus propias funciones asíncronas, puedes seguir este patrón básico:

function miOperacionAsincrona(parametro1, parametro2, callback) {
  // Validación de parámetros
  if (!parametro1) {
    process.nextTick(() => {
      callback(new Error('Parámetro 1 es requerido'));
    });
    return;
  }
  
  // Simulación de operación asíncrona
  setTimeout(() => {
    try {
      const resultado = /* cálculo que podría fallar */;
      callback(null, resultado);
    } catch (error) {
      callback(error);
    }
  }, 100);
}

El uso de process.nextTick() asegura que el callback se ejecute de forma asíncrona incluso cuando detectamos errores inmediatamente, manteniendo un comportamiento consistente.

Manejo de múltiples resultados

Cuando necesitas devolver múltiples valores, simplemente añades más parámetros después del error:

function obtenerEstadisticas(archivo, callback) {
  fs.stat(archivo, (err, stats) => {
    if (err) {
      callback(err);
      return;
    }
    
    const esDirectorio = stats.isDirectory();
    const tamaño = stats.size;
    const fechaModificacion = stats.mtime;
    
    callback(null, esDirectorio, tamaño, fechaModificacion);
  });
}

obtenerEstadisticas('./carpeta', (err, esDir, tamaño, fecha) => {
  if (err) {
    console.error('Error:', err);
    return;
  }
  
  console.log(`Es directorio: ${esDir}`);
  console.log(`Tamaño: ${tamaño} bytes`);
  console.log(`Última modificación: ${fecha}`);
});

El patrón error-first callback se ha convertido en un estándar de facto en el ecosistema de Node.js, proporcionando una forma consistente y predecible de manejar operaciones asíncronas. Aunque las Promises y async/await ofrecen alternativas más modernas, entender este patrón es fundamental para trabajar con el amplio conjunto de APIs y bibliotecas basadas en callbacks.

Limitaciones y problemas: Callback hell y estrategias para mejorar la legibilidad

A pesar de su utilidad, los callbacks presentan desafíos significativos cuando se utilizan en operaciones asíncronas complejas. El más notorio es el llamado "callback hell" (también conocido como "pyramid of doom" o pirámide de la perdición), un problema que afecta directamente a la legibilidad y mantenibilidad del código.

¿Qué es el callback hell?

El callback hell ocurre cuando tenemos múltiples operaciones asíncronas anidadas, cada una dependiente del resultado de la anterior. Esto crea un patrón de código que se desplaza horizontalmente, formando una estructura similar a una pirámide:

getUser(userId, (error, user) => {
  if (error) {
    console.error("Error obteniendo usuario:", error);
    return;
  }
  
  getPermissions(user.id, (error, permissions) => {
    if (error) {
      console.error("Error obteniendo permisos:", error);
      return;
    }
    
    getContent(permissions, (error, content) => {
      if (error) {
        console.error("Error obteniendo contenido:", error);
        return;
      }
      
      processContent(content, (error, result) => {
        if (error) {
          console.error("Error procesando contenido:", error);
          return;
        }
        
        console.log("Resultado final:", result);
      });
    });
  });
});

Este código presenta varios problemas evidentes:

  • Legibilidad reducida: La indentación excesiva dificulta seguir el flujo lógico
  • Manejo repetitivo de errores: Cada nivel requiere su propio bloque de manejo de errores
  • Dificultad de mantenimiento: Añadir o modificar pasos en la secuencia es propenso a errores
  • Complejidad para depurar: Rastrear errores a través de múltiples niveles de anidación es complicado

Estrategias para mejorar la legibilidad

Afortunadamente, existen varias técnicas efectivas para mitigar el callback hell, incluso antes de adoptar Promises o async/await:

1. Nombrar las funciones callback

En lugar de usar funciones anónimas, podemos definir funciones con nombre:

function getUserCallback(error, user) {
  if (error) {
    console.error("Error obteniendo usuario:", error);
    return;
  }
  
  getPermissions(user.id, getPermissionsCallback);
}

function getPermissionsCallback(error, permissions) {
  if (error) {
    console.error("Error obteniendo permisos:", error);
    return;
  }
  
  getContent(permissions, getContentCallback);
}

function getContentCallback(error, content) {
  if (error) {
    console.error("Error obteniendo contenido:", error);
    return;
  }
  
  processContent(content, processContentCallback);
}

function processContentCallback(error, result) {
  if (error) {
    console.error("Error procesando contenido:", error);
    return;
  }
  
  console.log("Resultado final:", result);
}

// Iniciar la secuencia
getUser(userId, getUserCallback);

Esta técnica mejora la legibilidad al eliminar la anidación y facilita la depuración al proporcionar nombres de función en la pila de llamadas.

2. Modularización en funciones pequeñas

Podemos dividir el flujo en funciones independientes que encapsulen cada paso:

function getUserData(userId, callback) {
  getUser(userId, (error, user) => {
    if (error) {
      callback(error);
      return;
    }
    callback(null, user);
  });
}

function getUserPermissions(user, callback) {
  getPermissions(user.id, (error, permissions) => {
    if (error) {
      callback(error);
      return;
    }
    callback(null, permissions);
  });
}

function getContentData(permissions, callback) {
  getContent(permissions, callback);
}

function processContentData(content, callback) {
  processContent(content, callback);
}

// Uso secuencial más limpio
getUserData(userId, (error, user) => {
  if (error) return handleError(error);
  
  getUserPermissions(user, (error, permissions) => {
    if (error) return handleError(error);
    
    getContentData(permissions, (error, content) => {
      if (error) return handleError(error);
      
      processContentData(content, (error, result) => {
        if (error) return handleError(error);
        console.log("Resultado final:", result);
      });
    });
  });
});

function handleError(error) {
  console.error("Error en el proceso:", error);
}

Esta aproximación mejora la organización del código y facilita la reutilización de componentes.

3. Uso de bibliotecas de control de flujo

Antes de que Promises se volvieran estándar, bibliotecas como async.js proporcionaban utilidades para gestionar flujos asíncronos:

const async = require('async');

async.waterfall([
  function(callback) {
    getUser(userId, callback);
  },
  function(user, callback) {
    getPermissions(user.id, callback);
  },
  function(permissions, callback) {
    getContent(permissions, callback);
  },
  function(content, callback) {
    processContent(content, callback);
  }
], function(error, result) {
  if (error) {
    console.error("Error en el proceso:", error);
    return;
  }
  console.log("Resultado final:", result);
});

Esta técnica elimina la anidación y proporciona un manejo de errores centralizado.

4. Patrón de paso de continuación (continuation-passing)

Este patrón consiste en pasar la lógica de continuación como un callback:

function fetchUserData(userId, continuation) {
  getUser(userId, (error, user) => {
    if (error) {
      console.error("Error obteniendo usuario:", error);
      return;
    }
    
    continuation(user);
  });
}

// Uso
fetchUserData(userId, (user) => {
  getPermissions(user.id, (error, permissions) => {
    if (error) {
      console.error("Error obteniendo permisos:", error);
      return;
    }
    
    // Continuar con el flujo...
  });
});

Este enfoque separa la lógica de error de la lógica principal, aunque no elimina completamente la anidación.

5. Evitar anidación excesiva con returns tempranos

Una técnica simple pero efectiva es usar returns tempranos para reducir la indentación:

getUser(userId, (error, user) => {
  if (error) {
    console.error("Error obteniendo usuario:", error);
    return;
  }
  
  // En lugar de anidar, continuamos en el mismo nivel
  getUserPosts(user.id, handleUserPosts);
});

function handleUserPosts(error, posts) {
  if (error) {
    console.error("Error obteniendo posts:", error);
    return;
  }
  
  // Procesamiento adicional
  console.log(`Encontrados ${posts.length} posts`);
}

Esta técnica mejora la legibilidad al evitar niveles adicionales de indentación.

Patrones avanzados para gestionar callbacks

Patrón de eventos para operaciones asíncronas

El patrón de eventos puede ser una alternativa a los callbacks anidados:

const EventEmitter = require('events');
const userProcessor = new EventEmitter();

// Configurar manejadores de eventos
userProcessor.on('got-user', (user) => {
  console.log("Usuario obtenido:", user.name);
  getPermissions(user.id, (error, permissions) => {
    if (error) {
      userProcessor.emit('error', error);
      return;
    }
    userProcessor.emit('got-permissions', permissions);
  });
});

userProcessor.on('got-permissions', (permissions) => {
  console.log("Permisos obtenidos");
  // Continuar el flujo...
});

userProcessor.on('error', (error) => {
  console.error("Error en el proceso:", error);
});

// Iniciar el proceso
getUser(userId, (error, user) => {
  if (error) {
    userProcessor.emit('error', error);
    return;
  }
  userProcessor.emit('got-user', user);
});

Este patrón desacopla las operaciones y permite manejar flujos más complejos, aunque introduce su propia complejidad.

Implementación de máquinas de estado

Para flujos muy complejos, una máquina de estado puede ser apropiada:

const stateMachine = {
  state: 'init',
  data: {},
  
  // Transiciones
  init() {
    this.state = 'fetching-user';
    getUser(userId, this.handleUser.bind(this));
  },
  
  handleUser(error, user) {
    if (error) {
      this.state = 'error';
      this.error = error;
      this.logError();
      return;
    }
    
    this.data.user = user;
    this.state = 'fetching-permissions';
    getPermissions(user.id, this.handlePermissions.bind(this));
  },
  
  handlePermissions(error, permissions) {
    if (error) {
      this.state = 'error';
      this.error = error;
      this.logError();
      return;
    }
    
    this.data.permissions = permissions;
    this.state = 'complete';
    this.processResult();
  },
  
  logError() {
    console.error(`Error en estado ${this.state}:`, this.error);
  },
  
  processResult() {
    console.log("Proceso completado con datos:", this.data);
  }
};

// Iniciar la máquina
stateMachine.init();

Este enfoque organiza el código según estados del proceso, facilitando el seguimiento del flujo lógico.

Limitaciones adicionales de los callbacks

Además del callback hell, los callbacks presentan otras limitaciones importantes:

  • Dificultad para manejar operaciones paralelas: Coordinar múltiples operaciones asíncronas simultáneas requiere código adicional
  • Problemas con el manejo de excepciones: Las excepciones lanzadas dentro de callbacks asíncronos no pueden capturarse con try/catch convencional
  • Dificultad para implementar patrones de control de flujo: Operaciones como retry, timeout o race son complejas de implementar
  • Inversión de control: Al usar callbacks, cedemos el control de la ejecución a la función que recibe nuestro callback
// Problema con excepciones en callbacks
try {
  setTimeout(() => {
    throw new Error("Este error no será capturado");
  }, 100);
} catch (error) {
  // Este bloque nunca se ejecutará
  console.error("Error capturado:", error);
}

Transición hacia soluciones modernas

Aunque las estrategias mencionadas mejoran el trabajo con callbacks, las soluciones modernas como Promises y async/await ofrecen ventajas significativas:

// Versión con Promises
getUserAsync(userId)
  .then(user => getPermissionsAsync(user.id))
  .then(permissions => getContentAsync(permissions))
  .then(content => processContentAsync(content))
  .then(result => {
    console.log("Resultado final:", result);
  })
  .catch(error => {
    console.error("Error en el proceso:", error);
  });

// Versión con async/await
async function processUserData(userId) {
  try {
    const user = await getUserAsync(userId);
    const permissions = await getPermissionsAsync(user.id);
    const content = await getContentAsync(permissions);
    const result = await processContentAsync(content);
    console.log("Resultado final:", result);
  } catch (error) {
    console.error("Error en el proceso:", error);
  }
}

Estas aproximaciones resuelven fundamentalmente muchos de los problemas inherentes a los callbacks, proporcionando código más legible, mejor manejo de errores y mayor facilidad para implementar patrones de control de flujo complejos.

A pesar de sus limitaciones, entender los callbacks y sus problemas es esencial para apreciar completamente las ventajas de las abstracciones modernas y para trabajar efectivamente con código legacy o APIs basadas en callbacks que aún son comunes en el ecosistema de JavaScript.

Aprende JavaScript online

Otros ejercicios de programación de JavaScript

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

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Proyecto Manipulación DOM

JavaScript
Proyecto

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Reto Métodos de Strings

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

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

Reto Funciones flecha

JavaScript
Código

Tipos de datos

JavaScript
Puzzle

Reto Operadores avanzados

JavaScript
Código

Promises

JavaScript
Código

Reto Estructuras de control

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Inmutabilidad y programación funcional pura

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Reto Polimorfismo

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Reto Variables

JavaScript
Código

Gestor de tareas con JavaScript

JavaScript
Proyecto

Proyecto Modificación de elementos DOM

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Conjuntos con Set

JavaScript
Código

Reto Prototipos y cadena de prototipos

JavaScript
Código

Reto Encapsulación

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Reto Excepciones

JavaScript
Código

Reto Filtrado con filter() y find()

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Reto Herencia

JavaScript
Código

Herencia

JavaScript
Puzzle

Proyecto Eventos del DOM

JavaScript
Proyecto

Herencia

JavaScript
Test

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Reto Clases y objetos

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Reto Destructuring de objetos y arrays

JavaScript
Código

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

Reto Funciones

JavaScript
Código

Reto Funciones cierre (closure)

JavaScript
Código

Promises

JavaScript
Test

Reto Reducción con reduce()

JavaScript
Código

Async / Await

JavaScript
Test

Reto Estructuras de control

JavaScript
Código

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

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

Reto Mapas con Map

JavaScript
Código

Funciones

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Reto Expresiones regulares

JavaScript
Código

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Array

JavaScript
Puzzle

Encapsulación

JavaScript
Puzzle

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

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

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

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

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

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

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

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

Api Fetch

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

  1. Comprender qué son y cómo funcionan las funciones de orden superior en JavaScript.
  2. Aplicar funciones de orden superior para mejorar patrones asíncronos.
  3. Implementar funciones que devuelvan otras funciones y funciones como valores.
  4. Manejar asincronía con callbacks usando funciones de orden superior.
  5. Mejorar la legibilidad y organización del código con funciones de orden superior.
  6. Implementar patrones de programación funcional aplicados a la asincronía.