Resumen

Comprender cómo funcionan los callbacks es fundamental para dominar la asincronía en JavaScript. Este patrón fue durante años la forma principal de manejar operaciones que no se ejecutan de inmediato, y aunque hoy existen alternativas como las promesas y async/await, los callbacks siguen presentes en el día a día de cualquier desarrollador.

¿Qué es un callback y por qué es tan importante?

Un callback es simplemente una función que se pasa como argumento a otra función, para que esa otra función la ejecute en el momento adecuado [0:14]. No es un concepto exclusivo de la asincronía; de hecho, lo usas constantemente sin darte cuenta.

Cuando una función recibe otra función como argumento o devuelve una función, se le conoce como higher-order function [0:28]. Métodos como forEach, map, filter y reduce son ejemplos perfectos de higher-order functions porque reciben un callback como parámetro.

¿Cómo se ve un callback en código real?

Veamos un ejemplo básico con forEach [0:42]:

javascript const numeros = [1, 2, 3];

numeros.forEach(function(numero) { console.log(numero); });

La función anónima que recibe forEach es el callback, y forEach es la higher-order function. Lo mismo se puede escribir con una arrow function:

javascript numeros.forEach(numero => console.log(numero));

Otros ejemplos comunes incluyen map y filter [1:17]:

javascript const dobles = numeros.map(n => n * 2); console.log(dobles);

const pares = numeros.filter(n => n % 2 === 0); console.log(pares);

En cada caso, la función que pasas como argumento es un callback que la higher-order function ejecuta internamente.

¿Cuál es la diferencia entre callbacks síncronos y asíncronos?

No todos los callbacks son asíncronos. Un callback síncrono se ejecuta de forma inmediata, uno por uno, bloqueando el hilo hasta que termina [1:42]. La diferencia clave está en quién llama al callback y cuándo.

Observa este ejemplo [1:55]:

javascript console.log('antes'); numeros.forEach(n => console.log(n)); console.log('después');

El resultado es predecible: primero "antes", luego los números en orden, y finalmente "después". Todo se ejecuta secuencialmente porque forEach es síncrono.

En cambio, un callback asíncrono no respeta ese orden lineal. Su ejecución depende del event loop, del call stack y de si la tarea es una macro task o una micro task [2:24]. Un setTimeout, por ejemplo, enviará su callback a la cola de macro tasks, y se ejecutará solo cuando el call stack esté vacío.

¿Cómo funcionan los callbacks asíncronos en la práctica?

Los callbacks asíncronos son la base de cómo JavaScript manejó la asincronía durante años [2:42]. La idea es directa: en lugar de esperar un resultado, le indicas a JavaScript qué función ejecutar cuando algo suceda.

javascript setTimeout(() => { console.log('Han pasado 2 segundos'); }, 2000);

document.getElementById('boton').addEventListener('click', () => { console.log('Clic detectado'); });

En ambos casos, tú defines la función y el entorno decide cuándo llamarla [3:08]: cuando pasen dos segundos, cuando el usuario haga clic, cuando se complete una petición HTTP.

¿Por qué los callbacks siguen siendo relevantes hoy?

Antes de las promesas, prácticamente toda la asincronía se resolvía con callbacks: lectura de archivos, peticiones HTTP, acceso a bases de datos y eventos del DOM [3:22]. El patrón siempre seguía tres pasos:

  • Llamas a una función.
  • Le pasas un callback.
  • Cuando termina la operación, se ejecuta el callback.

Los callbacks ofrecen una separación clara entre iniciar una tarea y reaccionar al resultado [3:40]. Siguen siendo útiles en casos simples y continúan presentes en muchas APIs.

Sin embargo, surge una pregunta natural: ¿qué pasa cuando necesitas encadenar varias operaciones asíncronas en secuencia? ¿Y qué ocurre cuando algo falla? Estas limitaciones son exactamente lo que las promesas y async/await resuelven. Si ya dominas los callbacks, estás listo para dar ese siguiente paso. ¿Qué patrón asíncrono prefieres usar en tus proyectos?