0% encontró este documento útil (0 votos)
45 vistas14 páginas

Funciones y Callbacks en NodeJS

Este documento describe las funciones y callbacks en Node.js. Explica cómo declarar funciones de diferentes maneras, incluido el uso de funciones anónimas. También describe cómo las funciones pueden recibir otras funciones como parámetros y el concepto de callbacks. Finalmente, cubre algunas convenciones comunes para funciones con callbacks.

Cargado por

Tadeo Converso
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
45 vistas14 páginas

Funciones y Callbacks en NodeJS

Este documento describe las funciones y callbacks en Node.js. Explica cómo declarar funciones de diferentes maneras, incluido el uso de funciones anónimas. También describe cómo las funciones pueden recibir otras funciones como parámetros y el concepto de callbacks. Finalmente, cubre algunas convenciones comunes para funciones con callbacks.

Cargado por

Tadeo Converso
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Funciones y Callbacks

Teoría

Introducción
En este apunte aprenderemos algunas particularidades de las funciones en NodeJS, que nos
permitirán aprovechar su capacidad al máximo.

Declaración de funciones
Las funciones NodeJS tienen varias particularidades con respecto a otros lenguajes. Veamos
todas las formas que ofrece el lenguaje para declarar una función

Estilo clásico

function​ ​mostrar​(params) {
​console​.log(params)
}

Llamada a la función:

mostrar(args)

Al ser NodeJS un lenguaje que no requiere especificar el tipo de dato de sus variables (tipado
dinámico), tampoco es necesario especificar el tipo de dato que devuelven las funciones, ni el
tipo de dato de los parámetros que éstas reciben.

Las funciones ​también​ son objetos


En NodeJS (y en JavaScript en general), las funciones se comportan como objetos, por lo que
es posible asignar una declaración de función a una variable. Si elegimos esta opción,
podemos elegir no escribir en forma explícita el nombre en la declaración de la función (esto es,
para evitar escribirlo dos veces: en la variable, y en la función).

const​ mostrar = ​function​(params) {


​console​.log(params)
}

Podemos ejecutarla de la misma manera en que lo hicimos en el ejemplo anterior.


Nuevo estilo (simplificado)
Las últimas versiones del lenguaje nos ofrecen una forma simplificada de declarar una función,
cuando la asignamos a una variable, obviando la palabra ‘function’, y agregando un operador
nuevo (esta forma de declarar funciones ofrece además algunas otras características que
veremos en mayor profundidad en otros apuntes).

La nueva sintaxis consiste en declarar únicamente los parámetros, y luego conectarlos con el
cuerpo de la función mediante el operador => (flecha gorda, o ‘fat arrow’ en inglés). Veamos un
ejemplo:

const​ mostrar = (params) => {


​console​.log(params)
}

La función se podrá usar de la misma manera que las anteriores.

En el caso de que la función reciba ​un solo​ parámetro, los paréntesis se vuelven opcionales,
pudiendo escribir:

const​ mostrar = params => {


​console​.log(params)
}

En el caso de que el cuerpo de la función conste de una única instrucción, las llaves se vuelven
opcionales, el cuerpo se puede escribir en la misma línea de la declaración (esto es así en
muchos otros lenguajes, no solo en NodeJS), y el resultado de computar esa única línea se
devuelve como resultado de la función, como si tuviera un “return” adelante. A esto se lo
conoce como “return implícito”. El ejemplo anterior se vería así:

const​ mostrar = params => ​console​.log(params)

Y en este caso la función devolvería “undefined” ya que console.log es de tipo void y por lo
tanto no devuelve nada. Un ejemplo, igualmente trivial, pero más explícito, de return implícito
sería el siguiente:

const​ promediar = (a, b) => (a + b) / 2


const​ p = promediar(4, 8) ​// 6
Funciones como parámetros
Como hemos visto, en NodeJS es posible asignar una función a una variable. Esto es porque
internamente, las funciones también son objetos (y las variables, referencias a esos objetos).

Es por esto que NodeJS nos permite hacer que una función reciba como parámetro una
referencia a otra función.

Veamos un ejemplo, utilizando lo que aprendimos en el punto anterior:

const​ ejecutar = unaFuncion => unaFuncion()


const​ saludar = () => ​console​.log(​'saludos'​)
ejecutar(saludar)

Y como ya sabemos: en donde puedo usar una variable, puedo usar también directamente el
contenido de esa variable:

ejecutar(() => ​console​.log(​'saludos'​))

En este ejemplo, la función ‘ejecutar’ recibe una función ​anónima​, y la ejecuta.


Como es de esperarse, esto también funciona con funciones anónimas con parámetros:

const​ ejecutar = (unaFuncion, params) => unaFuncion(params)


const​ saludar = nombre => ​console​.log(​`saludos, ​${nombre}​`)

ejecutar(saludar, ​'terricola'​)

Callbacks
Un callback es una función que se envía como argumento de otra función, con la intención de
que la función que hace de receptora ejecute la función que se le está pasando por parámetro.

De acuerdo a esta definición, podemos decir que la función “​ejecutar”​ que usamos en el punto
anterior “​recibe un callback​”.

Ahora bien, uno de los casos en que más se utiliza este recurso es el siguiente:

Imaginemos que queremos que al finalizar una operación se ejecute un cierto código. Por
ejemplo, queremos escribir un archivo, y queremos registrar en un log la hora en se termine de
escribir. Es probable que no se pueda saber con exactitud en qué momento va a finalizar. En
algunos casos (ya veremos en cuáles) no podemos simplemente ejecutar la operación de
escritura, y luego a continuación, guardar el log. En estos escenarios, las funciones deben
recibir como último parámetro un callback, que (por convención) será ejecutado al finalizar la
ejecución de la función.

Veamos una función inventada para ver cómo funciona:

const​ formatFecha = f =>


​`​${f.getDate()}​-​${f.getMonth()+​1​}​-​${f.getFullYear()}​`

// esta es mi función con callback


const​ escribirArchivo = (ruta, datos, callbackLog) => {
​// acá va la parte en donde se escriben los datos en
// el archivo, en la ruta especificada.
​// esta operación puede tardar un tiempo indeterminado.
​// al finalizar, ejecuta el código que sigue a continuación

​const​ fechaString = formatFecha(​new​ ​Date​())


callbackLog(fechaString, ​'grabación exitosa'​)
}

// esta función será mi callback!


const​ loguear = (fecha, mensaje) => ​console​.log(​`​${fecha}​: $
​ {mensaje}​`​)

// así es la llamada a la función


escribirArchivo(​'/ruta/al/archivo'​, ​'los datos'​, loguear)

Si mi función ‘escribirArchivo’ fuera la única que utiliza a la otra función ‘loguear’, en lugar de
declararla podríamos usar una ​función anónima,​ de la siguiente manera:

escribirArchivo(​'/ruta/al/archivo'​, ​'los datos'​, (fecha, mensaje) =>


​console​.log(​`​${fecha}​: ​${mensaje}​`​))

Algunas convenciones
Es costumbre dentro de la comunidad de programadores de NodeJS seguir una convención a
la hora de realizar funciones con callbacks. En la mayoría de los casos, éstas cumplen con las
siguientes características:

● El callback siempre es el ​último parámetro​.


● El callback suele ser una función que recibe ​dos parámetros​.
● La función llama al callback al terminar de ejecutar todas sus operaciones.
○ Si la operación resultó en un ​error​, la función llamará al callback pasando el
error obtenido como ​primer parámetro​.
○ Si la operación fue ​exitosa​, la función llamará al callback pasando ​null​ como
primer parámetro​.
○ Si la operación (​exitosa​) generó algún ​resultado​, éste se pasará al callback
como ​segundo parámetro​.

Desde el lado del callback, estas funciones deberán saber cómo manejar estos parámetros.
Por este motivo, nos encontraremos muy a menudo con la siguiente estructura (ejemplo):

const​ ejemploCallback = (error, resultado) => {


if (error) {
​// hacer algo con el error!
} ​else​ {
​// hacer algo con el resultado!
}
}

Callbacks anidados
A veces, debemos realizar varias de estas operaciones encadenadas, en serie. Lo que
mostramos a continuación, si bien no es muy una buena práctica (y más adelante veremos
formas de evitarlo) es un fragmento de código con el que nos podemos encontrar, en el cual
una función llama a un callback, y éste a otro callback, y éste a otro, y así sucesivamente. A
esto se le conoce como “​callback hell”​ (infierno de callbacks).

Ejemplo:

const​ copiarArchivo = (nombreArchivo, callback) => {


buscarArchivo(nombreArchivo, (error, archivo) => {
​if​ (error) {
callback(error)
} ​else​ {
leerArchivo(nombreArchivo, ​'utf-8'​, (error, texto) => {
​if​ (error) {
callback(error)
} ​else​ {
​const​ nombreCopia = nombreArchivo + ​'.copy'
escribirArchivo(nombreCopia, texto, (error) => {
​if​ (error) {
callback(error)
} ​else​ {
callback(​null​)
}
})
}
})
}
})
}
Objetos y JSON
Teoría

Cómo crear un objeto en javascript?


Simplemente escribiendo dos llaves { } ya tenemos un objeto en javascript. Es usual (pero no
obligatorio) guardar los objetos dentro de alguna variable:

Ejemplo

const persona = {}

Cómo agregarle propiedades a un objeto?


Simplemente haciendo nombreDelObjeto.nombreDeLaPropiedad = algunValor ...:

● Si la propiedad no existía, creamos la propiedad con el valor dado.


● Si la propiedad ya existía, actualizamos su valor.

Ejemplo

const persona = {}
persona.nombre = 'mariano'
persona.edad = 32

También podemos utilizar la siguiente sintaxis:

const persona = {}
persona['nombre'] = 'mariano'
persona['edad'] = 32

Esta sintaxis nos da la ventaja de que como el argumento que pasamos entre corchetes es un
string, este puede ser obtenido de diversas maneras, o incluso recibido por parámetro.

Cómo acceder a las propiedades a un objeto?


Al igual que para agregar, existen dos maneras de acceder a una propiedad de un objeto:

● Utilizando la notación: objeto.propiedad


● Utilizando la propiedad: objeto[propiedad]
Cualquiera de las dos variantes me devuelve el mismo resultado, que es el valor de la
propiedad en cuestión.

Ejemplo

console.log(persona.nombre)
console.log(persona['edad'])

Cómo quitarle propiedades a un objeto?


Para quitarle una propiedad a un objeto usaremos la palabra reservada delete y a
continuación la propiedad que queremos borrar, accediéndola desde su objeto contenedor:

Ejemplo

delete persona.edad

Propiedades anidadas
Es posible declarar propiedades que sean a su vez también objetos.

Ejemplo

const persona = {}
persona.nombre = 'mariano'
persona.edad = 32
persona.dirección = {}
persona.direccion.calle = 'rivadavia'
persona.direccion.numero = 1234
persona.telefonos = []
persona.telefonos.push('15-1234-5678')
persona.telefonos.push('15-1234-5000')

Declaración e inicialización en un solo paso


Nótese que al representar al objeto (mostrarlo) éste aparece como una serie de pares de datos,
separados por comas, todo dentro de las llaves que delimitan al objeto. Estos pares de datos
representan siempre una clave y un valor, es decir, el nombre del par, y el valor asociado a ese
nombre. El valor asociado puede ser cualquier cosa, incluyendo (no exclusivamente) números,
strings, objetos, arrays, etc.
Es posible declarar un objeto en js ya inicializándolo con sus valores, todo en una sola
operación. En este caso, se debe separar cada clave de cada valor usando dos puntos ( : ), y
cada par clave/valor del siguiente usando comas ( , ).
Ejemplo

const persona = {
nombre: 'mariano',
edad: 32,
direccion: {
calle: 'rivadavia',
numero: 1234
},
telefonos: ['15-1234-5678', '15-1234-5000']
}

JSON: JavaScript Object Notation


JSON es una forma de representar objetos de javascript como texto. Es considerada hoy una
de las formas de serialización de datos más utilizada. Es fácilmente interpretable ya que tiene
la misma estructura de pares clave/valor separados por dos puntos, y esos pares a su vez
separados por comas. La principal diferencia es que mientras que en los objetos de js las
propiedades se ven como variables (sin comillas), en json se escriben como strings, usando
comillas, y siempre usando comillas dobles ( " ).

Ejemplo

// Objeto en js:
const persona = {
nombre: 'mariano',
edad: 32
}

JSON válido

// forma indentada:
{
"nombre": "mariano",
"edad": 32
}

// forma compacta:
{ "nombre": "mariano", "edad": 32 }
JSON Inválido

// faltan comillas en las claves:


{
nombre: "mariano",
edad: 32
}

// las comillas de los strings deben ser dobles:


{
'nombre': 'mariano',
'edad': 32
}

// faltan las comas:


{
'nombre': 'mariano'
'edad': 32
}

// sobra la coma del último par clave/valor!:


{
'nombre': 'mariano',
'edad': 32,
}

Arrays en formato JSON


Es importante notar que no solo es posible tener objetos en formato JSON, sino también arrays
en dicho formato.

Ejemplo

// array de objetos en formato JSON válido


[
{
"nombre": "mariano",
"edad": 34
},
{
"nombre": "erica",
"edad": 36
}
]

Cómo convertir un objeto js a JSON y viceversa?


Javascript cuenta con una librería nativa JSON que nos permite tanto pasar de objeto a JSON y
de JSON a objeto.

Convertir de objeto a JSON


Se utiliza la función JSON.stringify( … ) que recibe un objeto y devuelve un string con la
representación de ese objeto en formato JSON.

Ejemplo

const persona = {
nombre: 'mariano',
edad: 32
}
console.log(JSON.stringify(persona))

Salida

{ "nombre": "mariano", "edad": 32 }

Si deseamos que el string salga con la indentación característica de JSON, podemos agregar
algunos parámetros a la llamada al stringify.
console.log(JSON.stringify(persona, null, 4))

Salida

{
"nombre": "mariano",
"edad": 32
}

(En este caso, el 4 representa la cantidad de espacios que se dejará como indentación entre
nivel y nivel).

Convertir de JSON a objeto


Se utiliza la función JSON.parse( … ) que recibe un string con la representación de un objeto
en formato JSON y devuelve el objeto correspondiente de js.
Ejemplo

const persona = JSON.parse('{ "nombre": "mariano", "edad": 32 }')


console.log(persona.edad)

Salida

32
Funciones, Objetos y JSON
Práctica

Preparación
Descargar la carpeta con los archivos de prueba y esqueleto de la solución desde el aula
virtual. Se trabajará únicamente sobre estos archivos, salvo que se indique lo contrario.

Desarrollar las siguientes funciones

actualizarArchivosDeudas
Recibe las rutas de cuatro archivos (de deudas viejo, de pagos, de deudas nuevo, y de log).
De los archivos de entrada sabemos lo siguiente:
● El archivo de deudas viejo no tiene un orden específico. Sus registros no presentan
repetidos de acuerdo a su dni, ni a su nombre. Todos sus campos poseen datos válidos
(no null, no vacíos). El campo ‘debe’ siempre es un número positivo.
● El archivo de pagos tampoco tiene un orden específico, y puede contener múltiples
registros con el mismo dni y apellido. El campo ‘fecha’ no se repite entre ninguno de los
registros. El campo ‘pago’ siempre es un número positivo.

Esta función no devuelve nada, y debe generar:


● Un archivo de deudas actualizado con todos los pagos realizados, a guardarse en la
ruta especificada como ‘deudas nuevo’. Este archivo debe tener las mismas
características que el de entrada (‘deudas viejo’).
● Un archivo de log, en la ruta provista, en donde quede un registro de los eventos
detallados en el último punto.

La función extrae el contenido de los archivos de la carpeta de entrada (in), los ordena, procesa
las actualizaciones de los pagos sobre las deudas, y graba el resultado final en un nuevo
archivo en la carpeta de salida (out).

ordenarPorClave
Recibe un array con objetos, y el nombre de un campo en formato string. Devuelve el array
original, ordenado en forma ascendente por el campo provisto.

Opcional
En lugar de un único string, recibe un array de strings. Devuelve el array original, ordenado en
forma ascendente por los campos provistos en el array de claves, siguiendo el mismo orden de
importancia (primera clave, primer campo que determina el orden, etc).
actualizarDeudas
Recibe un array con deudas, un array con pagos, y una función a la que podemos llamar,
pasandole un mensaje, cada vez que precisemos loguear un evento. Devuelve un array con las
deudas actualizadas según los pagos, siguiente el siguiente criterio:

● Si aparece un registro de pago con un dni que no coincide con el de ninguna deuda, ese
registro no se procesa, y se debe loguear como operación inválida.
● Si aparece un registro de pago que coincide con una deuda en su dni pero no en su
apellido, el mismo no se procesa, y se deben loguear ambos, la deuda y el pago.
● Si un registro de deuda no posee pagos asociados, se agrega directamente al array
actualizado, sin cambios.
● Si luego de aplicar todos los pagos correspondientes a una deuda, ésta queda aún
queda en positivo, se agrega al array actualizado con el nuevo importe.
● Si luego de aplicar todos los pagos correspondientes a una deuda, ésta queda en cero o
menos, la misma no debe agregarse al array de deudas actualizado.
● En particular, si la deuda queda en negativo (por debajo de cero), se deben loguear los
datos del cliente y cuánto saldo a favor tiene.

Observación
Para para facilitar la tarea, ya se cuenta con algunas funciones desarrolladas, y se proveen
también las firmas de las funciones pedidas, junto con su comentario correspondiente (formato
JSDOC). Además, se incluyen, como ejemplo, dos documentos de entrada con sus respectivos
documentos de salida, para usar como lote de prueba / verificación.

También podría gustarte