Los callbacks (a veces denominados funciones de retrollamada o funciones callback) no son más que un tipo de funciones que se pasan por parámetro a otras funciones. El objetivo de esto es tener una forma reutilizable de escribir funciones, que además entra bastante en consonancia con el concepto de asincronía de Javascript.
Ten en cuenta que actualmente, controlar la asincronía únicamente mediante callbacks puede ser una práctica poco recomendable. Es preferible utilizar promesas, que generalmente es lo más cómodo.
¿Qué es un callback?
Como hemos dicho, las funciones callback no son más que un tipo de funciones que se pasan por parámetro a otras funciones. Además, los parámetros de esos callbacks toman un valor especial en el interior de la función.
Pero para entenderlo bien, veamos un ejemplo. Imaginemos el siguiente bucle for
tradicional para recorrer un
const letters = ["A", "B", "C"];
for (let pos = 0; pos < letters.length; pos++) {
const letter = letters[pos];
console.log(`pos=${pos} letter=${letter}`);
}
En la variable pos
tenemos la posición del array que estamos recorriendo. Este valor irá desde 0
hasta 2
, mientras que en letter
guardamos letters[pos]
, es decir, la posición en el array, obteniendo el elemento, es decir, desde A
hasta C
:
pos=0 letter=A
pos=1 letter=B
pos=2 letter=C
Ahora veamos, como podemos hacer este mismo bucle utilizando el método forEach()
del action()
y ten en cuenta que los parámetros element
e index
son parámetros «especiales» al ser una función callback:
const letters = ["A", "B", "C"];
function action(element, index) {
console.log(`pos=${index} letter=${element}`);
}
// Por cada item del array, ejecuta action()
letters.forEach(action);
Esto se suele reescribir habitualmente de la siguiente forma:
letters.forEach((element, index) => {
console.log(`pos=${index} letter=${element}`);
});
Lo importante de este ejemplo es que se vea que la función callback que le hemos pasado por parámetro a forEach()
se va a ejecutar por cada uno de los elementos del array, y en cada iteración de dicha función callback, los parámetros element
e index
van a tener un valor especial:
element
es el elemento del arrayindex
es el índice (posición) del array
Callbacks en Javascript
Una vez entendido esto, vamos a profundizar un poco con las funciones callbacks utilizadas para realizar tareas asíncronas. Probablemente, el caso más fácil de entender es utilizar un temporizador mediante la función setTimeout(
callback,
time)
.
Dicha función nos exige dos parámetros:
- La función
callback
a ejecutar - El tiempo
time
que esperará antes de ejecutarse
Observa el siguiente ejemplo. Simplemente, le decimos a setTimeout()
que ejecute la función callback que le hemos pasado por primer parámetro cuando transcurran 2000
milisegundos (es decir, 2 segundos):
function action() {
console.log("He ejecutado la función");
}
setTimeout(action, 2000);
Utilizando arrow functions y escribiendo la función directamente, se puede simplificar el callback y hacerlo más fácil de escribir:
setTimeout(() => console.log("He ejecutado la función"), 2000);
En cualquiera de los casos, lo importante es darse cuenta que estamos usando una función callback para pasársela a setTimeout()
, que es otra función. En este caso, se trata de «programar» un suceso que ocurrirá en un momento concreto del futuro, pero muchas veces desconociendo cuando se producirá exactamente (o incluso si se llegará a producir).
Orden de ejecución
Si probamos el siguiente código en una consola del navegador, comprobarás que el segundo console.log()
se ejecutará antes que el primer console.log()
que está dentro del setTimeout()
, mostrando primero Código síncrono
y luego Código asíncrono
:
setTimeout(() => {
console.log("Código asíncrono.");
}, 2000);
console.log("Código síncrono.");
El último console.log()
del código se ejecuta primero (forma parte del flujo principal de ejecución del programa). El setTimeout()
que figura en una línea anterior, aunque se ejecuta antes, pone en espera a la función callback, que se ejecutará cuando se cumpla una cierta condición (en este caso, cuando transcurran 2 segundos desde ese momento).
Esto puede llegar a sorprender a desarrolladores que llegan de otros lenguajes considerados bloqueantes; Javascript sin embargo se considera un lenguaje que puede escribir código asíncrono y no bloqueante. ¿Qué significa esto? Al ejecutar la línea del
setTimeout()
, el programa no se queda bloqueado esperando a que terminen los 2 segundos y se ejecute la función callback, sino que continúa con el flujo general del programa para volver más adelante cuando sea necesario a ejecutar la funcióncallback
, aprovechando así mejor el tiempo y realizando tareas de forma asíncrona.