Js Callbacks y cómo usarlos

En este artículo hablaré de que es un callback y de su anatomía, de manera sencilla, con ejemplos y para puedas utilizarlos en tu día a día.

Un ejemplo sencillo

Imaginemos que tenemos una función que te multiplica x 2 un número pasado por parámetro y que una vez termine queremos que realice otra acción, como por ejemplo sumarle un número.

Normalmente lo que haríamos sería algo así:

function calculaDobleMasNum(numToDoble, numToSum ) {  
return (numToDoble * 2) + numToSum
}
console.log(calculaDobleMasNum( 3, 5)); // (3 * 2) + 5 = 11

Pero qué pasaría si tuviéramos que sumarle un número en vez de dividirlo o si quisiéramos imprimirlo por pantalla, o calcular su factorial… Cada vez que queramos añadirle una funcionalidad extra tendríamos que crear una función nueva que lo hiciera

function doble(num) {
return (num * 2) + numSum
}

function dobleSuma5(num) {
return (num * 2) + 5
}
function dobleMenos3(num) {
alert(num * 2) - 3
}

console.log(doble(3)); // (3 * 2) + 5 = 11

console.log(dobleSuma5(3)); // (3 * 2) - 5 = 1

console.log(dobleMenos3(3)); // Una ventana con el numero 6

Nos queda un poco repetitivo por lo que deberíamos es separar cada operación en pequeñas funciones, con el fin de no repetir el * 2 en todas las funciones.

function doble(num) {
return numToDoble * 2
}

function suma5(num) {
return num + 5
}
function menos3(num) {
return num - 3
}
console.log( suma5(doble(3)) ); 

console.log( menos3(doble(3)) );

De esta manera es mas sencilla , pero el orden de ejecución, resulta un poco extraño ya que por ejemplo cuando llamamos a suma5(doble(3))

lo primero que vemos

 suma5(…) 

es lo último que se va a ejecutar

y lo último que vemos

doble(3) 

que es lo primero que se ejecutará

Por supuesto nuestras cabezas pensantes, acostumbradas a la programación, resuelven estas cosas de manera sencilla y sabemos en todo momento el orden de ejecución ¿no?

¿Que es un callback?

Un callaback o retrollamada es una función que ejecuta otra función pasada por parámetro una vez que se resuelve la primera.

En otras palabras cuando se termina de ejecutar una función, esta ejecuta otra que le has pasado por parámetro, ¿sencillo no?

Para verlo más claro vamos al ejemplo anterior

function doble(num) {
return numToDoble * 2
}

function suma5(num) {
return num + 5
}
function menos3(num) {
return num - 3
}
console.log( suma5(doble(3)) ); // (3 * 2) + 5 = 11

console.log( menos3(doble(3)) ); // (3 * 2) - 5 = 1

usando callbacks sería así:

function doble(num, callback) {
return callback(num * 2);
}

function suma5(num) {
return num + 5
}
function menos3(num) {
return num - 3
}
console.log( doble(3, sum5)) );

console.log( doble(3, menos3)) );

De esta manera sabemos, que primero se va a ejecutar el doble del numero 3 y luego le va a sumar 4 o restar 3 según queramos. Incluso podríamos ahorrarnos las funciones suma5 y menos3 para tener menos líneas:

function doble(num, callback) {
return callback(num * 2);
}
console.log( doble(3, function(num) { return num + 5 }) );

console.log( doble(3, function(num) { return num - 3 }) );

Por no hablar de que si conocemos las arrow functions de ES6 podríamos hacer algo asi:

const doble = (num, callback) => callback(num * 2);  
console.log( doble(3, num => num + 5 ) );

console.log( doble(3, num => num - 3 ) );

Aplicándolo al mundo real JS

Actualmente el lenguaje javascript tiene unas cuantas funciones que utilizan callbacks, de entre las mas utilizadas podemos encontrar la función find de los arrays, la cual te permite buscar el primer elemento que cumpla con los requisitos. Para ello find va pasando a la función callback cada uno de los elementos del array hasta que la función callback le devuelva un valor verdadero. En el caso de que se recorra todos los elementos del array y el callback no devuelva verdadero en ninguno de los casos, devolverá undefined.

Por ejemplo, tenemos una lista de números y queremos encontrar el primer número que sea mayor de 10.

var list = [5, 12, 8, 130, 44];
var found = list.find(function(element) {
return element > 10;
});
console.log(found); // expected output: 12