- 1. Definición e invocación
- 2. Funciones anónimas
- 3. Formas de ejecutar una función anónima
- 4. Valor devuelto o valor de retorno
- 5. Parámetros y argumentos
- 6. Ejecución asíncrona
- 7. Referencias
function suma (a, b) { return a + b; } // Declaración o definición
suma (2, 3); // Llamada o invocación
typeof suma; // "function"A tener en cuenta:
- En la declaración de una función indicamos los parámetros formales, si los tiene. En este caso
ayb. - En la llamada a la función indicamos los parámetros reales o argumentos. En este caso
2y3.
- No tienen nombre, así que en principio no son invocables.
- Suelen utilizarse en circunstancias donde sólo se invoca a dicha función una única vez.
// forma tradicional
function (a, b) { return a + b; } // ¿Cómo hago una llamada a esta función??
// forma de función flecha
(a, b) => { return a + b; } // expresada en forma de función flecha
(a, b) => a + b; // expresada en forma de función flecha, simplificando return- Es una forma de declarar e invocar una función, todo en un sólo paso.
- La declaración de la función se encierra entre paréntesis
( ) - A continuación los argumentos también se encierran entre paréntesis
( )
(function (a, b) { return a + b; }) (2, 3); const suma = function (a, b) { return a + b; } ; // puedo asignarla a una variable
suma (2, 3); // ahora sí puedo invocar la función a través de la variable asociadaUn callback es una función que es pasada como argumento a otra función. Dos funciones muy conocidas en Javascript, que necesitan un callback son:
setTimeout ( callback, milisegundos); // Ejecuta el callback una sola vez después de transcurridos los ms indicados
setInverval ( callback, milisegundos); // Ejecuta el callback de forma recurrente cada cierto tiempo indicado en ms
Ejemplo
// Función que usaremos para el callback. En este caso, NO es una función anónima.
function hola(){
console.log ('Hola');
}
// La función hola será pasada como callback.
let t1 = setTimeout (hola, 5000); // para cancelar ejecutamos clearTimeout (t1) antes del que el temporizador expire
let t2 = setInterval(hola, 5000); // para cancelar ejecutamos clearInterval (t2) en cualquier momentoUna forma más compacta de escribir el código anterior es usando funciones anónimas:
let t1 = setTimeout ( function () { console.log ('Hola') }, 5000 ); // Hacemos uso de función anónima
let t2 = setInterval ( function () { console.log ('Hola') }, 5000 ); // Hacemos uso de función anónimaEl caso anterior se puede simplificar aún más haciendo uso de funciones flecha (arrow functions):
let t1 = setTimeout ( () => console.log ('Hola') , 5000 ); // Hacemos uso de función anónima
let t2 = setInterval ( () => console.log ('Hola') , 5000 ); // Hacemos uso de función anónimaPara cancelar setTimeout y setInterval usaremos las funciones clearTimeout y clearInterval. Ejemplo:
clearTimeout(t1);
clearInterval(t2);Un caso específico a las funciones setTimeout y setInterval es que se le pueden pasar más argumentos. Todos los argumentos que aparezcan detrás de los milisegundos serán argumentos que serán pasados al callback. Por ejemplo, a continuación tenemos una función suma que utilizaremos como callback. Dicha función necesita dos argumentos para poder operar. Estos argumentos son los que escribimos como 3er y 4o argumento de setTimeout.
// Función que usaremos para el callback
function suma (a, b) { console.log ( a + b); }
setTimeout ( suma // función a ejecutar
, 3000 // milisegundos a esperar antes de ejecutar la función
, 2 // primer argumento de la función
, 3 // segundo argumento de la función
) ; Una función de Javascript puede devolver:
- Nada
- Un valor de cualquier tipo (simple o compuesto)
- Una función
A continuación se muestra un ejemplo de cada caso.
No devuelve ningún valor
function sumar (a,b) {
console.log(`La suma de ${a} y ${b} es ${a+b}`)
}
sumar(3,5) // La suma de 3 y 5 es 8Devuelve un valor
function sumar (a,b) {
return a+b
}
sumar(3,5) // 8Devuelve una función
function calcular( operacion ) {
switch (operacion) {
case 'suma': return (a,b) => a + b;
case 'resta': return (a,b) => a - b;
case 'multiplicacion': return (a,b) => a*b;
case 'division': return (a,b) => a/b;
default: return () => {}
}
}
const sumar = calcular('suma')
const restar = calcular('resta')
sumar(3, 5) // 8
restar(3, 5) // -2
// También podemos ejecutar
calcular('suma')(3,5) // 8
calcular('resta')(3,5) // -2// Parámetros: a, b
// Tambien llamados párametros formales
function suma (a, b) {
return a + b;
}
let x=3, y=5;
// Argumentos: x, y
// Tambien llamados párametros reales
suma (x, y);Javascript es un lenguaje que realiza comprobaciones menos estrictas que otros lenguajes. Esto puede a veces provocar errores en programadores que no conocen bien el lenguaje.
Los parámetros no exigen un tipo a los argumentos Es posible llamar a la función suma con argumentos de tipo string u cualquier otro tipo.
suma ('Hola ', 'mundo'); // Devuelve 'Hola mundo'El número de argumentos puede ser menor que el número de parámetros declarados Los argumentos ausentes adquieren valor undefined.
suma ('Hola '); // Devuelve 'Hola undefined'
suma (2); // Devuelve NaNEl número de argumentos puede ser mayor que el número de parámetros declarados Es posible acceder a todos los argumentos, incluidos los excedentes, a través de la variable local arguments disponible para cada función;
suma ('Esto ', 'es ', 'una ', 'prueba.'); // Devuelve 'Esto es'Pero si redefinimos la función como
function suma (a, b) {
let s = 0;
for (let i = 0; i < arguments.length; i++) {
s = s + arguments[i];
}
return s;
} El resultado sería
// Funciona para cualquier número de argumentos
suma (2, 3, 4, 5, 6); // Devuelve 20
suma ('Esto ', 'es ', 'una ', 'prueba.'); // Devuelve '0Esto es una prueba.'NOTA: El objeto arguments no es un array aunque se comporte como tal, es decir no podemos aplicarle los métodos disponibles a arrays como sort, map, ...
Los argumentos de una función pueden pasarse de dos maneras:
Paso por valor
- Se hace una copia del valor de la variable pasada a la función.
- La función no puede modificar el valor de la variable original.
- Se usa con tipos de datos básicos.
let a = 1
function f1 ( num ) {
num++;
console.log ( num )
}
f (a) // 2
a // 1 // No se modifica el valor de la variable originalPaso por referencia
- No se hace copia del valor de la variable pasada a la función. Lo que se copia es la referencia o dirección de memoria donde se almacenan los datos.
- La funcion puede modificar el valor de la variable original.
- Se usa con arrays, objetos y funciones.
const b = [ 1, 2 ]
function f2 ( array ) {
array[0]++
console.log( array )
}
f2 ( b ) // [ 2, 2 ]
b // [ 2, 2 ] // Se modifica el valor de la variable originalUtilizada frecuentemente en operaciones de Entrada/Salida:
- Lectura y escritura de información en archivos (E/S archivos)
- Lectura y escritura de información en base de datos (E/S base de datos)
- Envío y recepción de información en red (E/S red)
La ejecución asíncrona es el método que nos permite solicitar a un agente externo la realización de una operación y, mientras esperamos su respuesta, seguimos con la ejecución del resto de nuestro código. Es habitual, cuando llega la respuesta, que puede ser de éxito o de error, detener momentánemente las operaciones que estabamos haciendo y ejecutar cierto código de tratamiento de la respuesta. Este código de tratamiento se ejecuta de forma asíncrona, puesto que no sabemos el momento en el que recibiremos la respuesta.
Existen 3 formas de ejecutar código de forma asíncrona.
- Callbacks
- Promesas
- async / await
Es la forma clásica, todavía ampliamente usada.
Cuando usamos callbacks el código tiene la siguiente forma:
hazAlgo (exitoCallback);Ejemplo 1
function callback (pos) {
console.log (pos.coords.latitude, pos.coords.longitude);
}
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition (callback);
} else {
console.log ("No hay soporte de GPS");
}Otras veces podemos encontrar la siguiente forma:
hazAlgo (exitoCallback, falloCallback);Ejemplo 2
Un mismo método o función puede definir más de un callback. A continuación el método getCurrentPosition con 2 callbacks. Se muestran 3 formas de uso.
Forma 1: con funciones nominadas
function successCallback (pos) {
console.log (pos.coords.latitude, pos.coords.longitude);
}
function errorCallback (error) {
console.log ('ERROR(' + error.code + '): ' + error.message);
}
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
} else {
console.log ("No hay soporte de GPS");
}Forma 2: con funciones anónimas
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
function (pos) { console.log( pos.coords.latitude, pos.coords.longitude); }
, function (err) { console.log('ERROR(' + err.code + '): ' + err.message); }
);
} else {
console.log ("No hay soporte de GPS");
}Forma 3: con funciones flecha
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
(pos) => console.log( pos.coords.latitude, pos.coords.longitude)
, (err) => console.log('ERROR(' + err.code + '): ' + err.message)
);
} else {
console.log ("No hay soporte de GPS");
}El inconveniente principal que tiene es que cuando necesitamos realizar varias operaciones asíncronas una tras otra, el código se sangra muy hacia la derecha, provocando lo que se conoce como callback hell y empeorando su legibilidad.
setTimeout (function () {
console.log('Este es el primer mensaje');
setTimeout (function () {
console.log('Este es el segundo mensaje');
setTimeout (function () {
console.log('Este es el tercer mensaje');
setTimeout (function () {
console.log('Este es el cuarto mensaje');
}, 800);
}, 2000);
}, 1500);
}, 1000);ATENCIÓN: El código anterior no es igual al mostrado a continuación.
setTimeout(function () { console.log('Este es el primer mensaje') }, 1000);
setTimeout(function () { console.log('Este es el segundo mensaje')}, 1500);
setTimeout(function () { console.log('Este es el tercer mensaje') }, 2000);
setTimeout(function () { console.log('Este es el cuarto mensaje') }, 800);Disponible a partir de ES6 (2015). La estructura del código es más clara que el uso de callbacks.
Algunos métodos soportan el uso de promesas.
Una promesa (Promise) es un objeto que representa la terminación o el fracaso eventual de una operación asíncrona. Por tanto es un valor que será devuelto en un futuro.
Cuando usamos promesas el código tiene la siguiente forma:
hazAlgo ()
.then (exitoCallback)
.catch (falloCallback);Ejemplo 1
Forma 1: con funciones nominadas
let URL = 'https://api.github.com/users/jamj2000';
function callback(response) {
console.log (response);
}
function error(err) {
console.log(`Error devuelto: ${err.code} - ${err.message}`);
}
fetch(URL)
.then(callback)
.catch(error);Explicación del código anterior:
- El método
fetchnos permite obtener un recurso de red de forma asíncrona. - Este método devuelve un objeto de la clase (Promise), es decir una promesa.
- Los objetos de la clase Promise disponen de dos métodos:
thencuando la promesa se resuelve de forma satisfactoria (resolved) o no satisfactoria (rejected).catchcuando la promesa no puede resolverse.
En este caso pueden darse 3 situaciones:
- Tenemos conexión a Internet y la URL https://api.github.com/users/jamj2000 existe. La promesa se resuelve de forma satisfactoria.
Response {type: "cors", url: "https://api.github.com/users/jamj2000", redirected: false,status: 200, ok: true,…} - Tenemos conexión a Internet y la URL https://api.github.com/jamj2000 no existe. La promesa se resuelve de forma no satisfactoria.
Response {type: "cors", url: "https://api.github.com/jamj2000", redirected: false,status: 404, ok: false,…} - No tenemos conexión a Internet. La promesa no puede resolverse.
Error devuelto: undefined - Failed to fetch
Forma 2: con funciones anónimas
let URL = 'https://api.github.com/users/jamj2000';
fetch(URL)
.then(function(response) {
console.log (response);
})
.catch(function(error) {
console.log(`Error devuelto: ${error.code} - ${error.message}`);
});Forma 3: con funciones flecha
let URL = 'https://api.github.com/users/jamj2000';
fetch(URL)
.then((response) => console.log (response))
.catch((error) => console.log(`Error devuelto: ${error.code} - ${error.message}`));Un código más práctico es el siguiente.
let URL = 'https://api.github.com/users/jamj2000';
fetch(URL)
.then ((response) => response.json() )
.catch((error) => `Error devuelto: ${error.code} - ${error.message}` );Es posible distinguir cuando una promesa de resuelve de forma satisfactoria (resolved) y se resuelve de forma no satisfactoria. La forma que adquiere el código tiene la siguiente forma:
hazAlgo ()
.then (exitoCallbackResolve, exitoCallbackReject)
.catch (falloCallback);Ejemplo 2
Forma 1: con funciones nominadas
let URL = 'https://api.github.com/users/jamj2000';
// En este ejemplo las 2 funciones siguientes se comportan de la misma manera
function exitoCallbackResolve (res) {
console.log (res.status);
}
function exitoCallbackReject (res) {
console.log (res.status);
}
function error (err) {
console.log(`Error devuelto: ${error.code} - ${error.message}`);
}
fetch(URL)
.then(exitoCallbackResolve, exitoCallbackReject)
.catch(error);Forma 2: con funciones anónimas
let URL = 'https://api.github.com/users/jamj2000';
fetch(URL)
.then(function(resolve) {
console.log (resolve.status);
},
function(reject) {
console.log (reject.status);
})
.catch(function(error) {
console.log(`Error devuelto: ${error.code} - ${error.message}`);
});Forma 3: con funciones flecha
let URL = 'https://api.github.com/users/jamj2000';
fetch(URL)
.then((resolve) => console.log (resolve.status) , (reject) => console.log (reject.status) )
.catch((error) => console.log(`Error devuelto: ${error.code} - ${error.message}`));Disponible a partir de ES7 (2016).
Se puede declarar una función como async. Las funciones async son esencialmente una forma más limpia de trabajar con código asíncrono en JavaScript.
Las funciones async pueden hacer uso de la expresión await. Esta expresión se antepone antes de la llamada a una función que devuelve una Promesa. Así se pausará la función async y esperará a que la Promesa se resuelva antes de continuar.
async function hazAlgo () {
...
await esperaQueAcabeYoAntesDeSeguir();
...
};Ejemplo 1
let URL = 'https://api.github.com/users/jamj2000';
async function mostrar() {
console.log("Inicio de recuperación de datos");
await fetch(URL)
.then((resolve) => console.log(resolve.status), (reject) => console.log(reject.status))
.catch((error) => console.log(`Error devuelto: ${error.code} - ${error.message}`));
console.log("Finalización de recuperación de datos");
}
mostrar();Resultado:
Inicio de recuperación de datos
Promise {<pending>}
200
Finalización de recuperación de datos
Si el código hubiera sido:
let URL = 'https://api.github.com/users/jamj2000';
function mostrar() {
console.log("Inicio de recuperación de datos");
fetch(URL)
.then((resolve) => console.log(resolve.status), (reject) => console.log(reject.status))
.catch((error) => console.log(`Error devuelto: ${error.code} - ${error.message}`));
console.log("Finalización de recuperación de datos");
}
mostrar();El resultado habría sido:
Inicio de recuperación de datos
Finalización de recuperación de datos
undefined
200
Ejemplo 2
Es mucho más habitual encontrar el código anterior escrito de la siguiente forma:
let URL = 'https://api.github.com/users/jamj2000';
async function mostrar() {
console.log("Inicio de recuperación de datos");
try {
const response = await fetch(URL);
const data = await response.json();
console.log(data)
}
catch (error) {
console.log(`Error devuelto: ${error.code} - ${error.message}`);
}
console.log("Finalización de recuperación de datos");
}
mostrar();Ejemplo 3
async function mensajes() {
await mostrar('Este es el primer mensaje', 1000);
await mostrar('Este es el segundo mensaje', 1500);
await mostrar('Este es el tercer mensaje', 2000);
await mostrar('Este es el cuarto mensaje', 800);
}
// Debe devolver una promesa, puesto que se va a usar dentro de una función async
function mostrar(mensaje, msEspera) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(mensaje.toUpperCase()); resolve();
}, msEspera)
});
}
mensajes();Ejemplo 4
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function mensajes () {
await sleep(1000); console.log('Este es el primer mensaje')
await sleep(1500); console.log('Este es el segundo mensaje')
await sleep(2000); console.log('Este es el tercer mensaje')
await sleep(800); console.log('Este es el cuarto mensaje')
}
mensajes()