Anuncios

Bienvenidos sean a este post, vamos a ver un evento con el ejemplo del post anterior.

Anuncios
Anuncios

En el post anterior creamos nuestra primera aplicacion para calcular el valor de la secuencia fibonacci de un numero informado, si le pedimos que lo haga para un numero mayor a 60 podremos cocinar nuestra comida, comerla y disfrutar de un tiempo siempre y cuando esto no «colgo» nuestro navegador, es mas si probamos de abrir dos ventanas de navegadores y en la primera le pedimos que calcule el valor de 60 y en la segunda un valor de 10 notaremos que la segunda ventana se colgara y no respondera hasta que la primera termine y cuando reaccione este lo hara inmediatamente, el problema es que se bloqueo al evento loop con el algoritmo fibonacci que al momento de ejecutarse no permite que este se produzca, esto es asi porque node.js tiene un solo thread de ejecucion y esto hace que las solicitudes de procesamiento dependan de cuan rapido los manejadores de solicitudes vuelvan rapido al evento loop pero afortunadamente la asincronicidad nos permite que el evento loop se ejecute regularmente.

Anuncios
Anuncios

Por ejemplo, cuando cargamos datos desde un servidor este es mediante una solicitud asincronica lo cual no bloquea el control y nos devuelve rapidamente al evento loop, como la funcion fibonacci no encaja en este modelo sino que la ejecuta hasta su final nos hace la conducta comentada anteriormente, y esto debemos evitarlo si deseamos tener un servidor web rapido y eficiente, por esta razon debemos evitar tiempos largos de respuesta y para observar los que suceden en nuestra aplicacion, en caso de no tenerlo les dejo un link con el proyecto de nuestro post anterior:

Anuncios

Extraigan todos los archivos en un directorio y con eso la aplicacion ya estara listo para ser usado, en el directorio raiz de nuestra aplicacion junto al archivo math.js crearemos uno nuevo llamado tiempofibo.js y le agregaremos el siguiente codigo:

tiempofibo.js

const math = require('./math');

for(var i = 0; i < 80; i++)
{
        let ahora = new Date().toISOString();
        console.log(`${ahora} Fibonacci para ${i} = ${math.fibonacci(i)}`);
}
Anuncios

Primero importaremos el modulo de math para usar la funcion fibonacci, despues mediante un bucle for contraremos de 0 a 80,, primero definiremos una variable que almacenara la hora y fecha actuales, despues mostraremos en pantalla el tiempo actual registrado en la variable anterior y despues mostraremos el resultado de aplicar la funcion fibonacci a cada valor de la pasada del bucle, un codigo simple para ver como trabaja actualmente la funcion fibonacci, para ello usaremos el siguiente video

Anuncios

En el video pueden ver que hasta el valor 35 se hicieron inmediatamente y a partir de este se ralentizo el calculo de cada uno, pero al final tuvimos que cancelarlo sin poder llegar al valor 80, para resolver esto tenemos dos formas:

  • Refactorizacion algoritmica, se podria considerar como dividir en callbacks despachadas a traves del evento loop
  • Crear un servicio de backend, se pasa todo el trabajo a servidores en backend quitando la tarea a los servidores de frontend
Anuncios

Sobre el segundo veremos un ejemplo mas adelante en otro post pero sobre el primero lo implementaremos a nuestro algoritmo encargado de calcular el valor fibonacci y para ello debemos ir al archivo math.js y agregaremos el siguiente bloque de codigo:

exports.fiboloop = function(n) {
        var fibos = {};
        fibos[0] = 0;
        fibos[1] = 1;
        fibos[2] = 1;
        for(var i = 3; i <= n; i++) {
                fibos[i] = fibos[i-2] + fibos[i-1];
        }
        return fibos[n];
};
Anuncios
Anuncios

En este caso tenemos una funcion donde recibira como argumento el valor a calcular, pero usaremos un array para almacenar todos los valores y este nos permite asignar en las tres primeras posiciones los valores que corresponden al posible valor que recibimos como argumento pero con valores superiores usaremos un bucle for donde aplicaremos la operacion para calcular el valor, para finalmente devolver el valor almacenado en la posicion del array en base al valor pasado como argumento, nuestro siguiente paso sera ir al archivo tiempofibo.js y modificaremos el codigo de la siguiente manera:

const math = require('./math');

for(var i = 0; i < 80; i++)
{
        let ahora = new Date().toISOString();
        console.log(`${ahora} Fibonacci para ${i} = ${math.fiboloop(i)}`);
}
Anuncios

En este caso hicimos un pequeño cambio, en lugar de llamar a la funcion fibonacci llamamos a la nueva funcion, fiboloop, probemos como funcion ahora nuestro script

Anuncios
Anuncios

A diferencia del caso anterior este funciono perfectamente e inclusive si cambian el valor del bucle 80 a 2000 o 4000, veran que funciona perfectamente y si bien tendra una demora este si terminara en un tiempo relativamente corto comparado con el anterior pero seguimos con el mismo inconveniente porque si bien la demora es corta si hacemos dos o mas ejecuciones al mismo tiempo las finales no se procesaran hasta que terminen las primeras, tomemos nuevammente el archivo math.js y agreguemos el siguiente bloque de codigo:

module.exports.fiboasync = function(n, done) {
        if(n===0) done(undefined, 0);
        else if(n === 1 || n === 2) done(undefined, 1);
        else {
                setImmediate(() => {
                        exports.fiboasync(n-1, (err, val1) => {
                                if (err) done(err);
                                else setImmediate(() => {
                                        exports.fiboasync(n-2, (err, val2) => {
                                                if (err) done(err);
                                                else done(undefined, val1+val2);
                                        });
                                });
                        });
                });
        }
};
Anuncios
Anuncios

Esta sera una funcion para calcular a fibonacci pero de forma asincronica, si lo debemos comparar es muy similar al original porque volvemos a usar una funcion redundante donde haremos todo el ciclo de la secuencia fibonacci pero en este caso usamos a setInmediate el cual nos permitira ejecutarlo en el evento loop tan pronto como sea posible, esto es ideal para cuando necesitamos que la funcion sea asincronica, observen que los primeros dos condicionales son para los primeros tres valores de fibonacci, nuestro siguiente paso sera ir al archivo tiempfibo.js y modificaremos el codigo de la siguiente manera:

const math = require('./math');

(async () => {
        for(var i=1; i < 80; i++) {
                await new Promise((resolve, reject) => {
                        math.fiboasync(i, (err, fibo) => {
                          if (err) reject(err);
                          else {
                            let ahora = new Date().toISOString();
                            console.log(`${ahora} Fibonacci para ${i} = ${fibo}`);
                            resolve();
                          }
                        })
                })
        }
})().catch(err => { console.log(err); });
Anuncios
Anuncios

Ahora podemos utilizarlo de forma asincronica mediante async y en este repetiremos el bucle de 80, esperaremos una promesa y en este ejecutaremos la funcion creada anteriormente y la funcion de callback se detendra ante algun error y en caso contrario nos tomara la hora y fecha actual, lo mostrara en conjunto con el valor fibonacci y pasara el resolve, por ultimo tenemos un catch para mostrar el eventual error, si lo prueban deberan tener un resultado similar al visto en el primer caso pero porque volver al mismo error? bueno, en este caso es por la asincronicidad que nos permitira hacer otra tarea, para entenderlo vamos a crear un nuevo archivo en el directorio routes con el nombre de fibo-async.js y le agregaremos el siguiente codigo:

routes/fibo-async.js

var express = require('express');
var router = express.Router();

const math = require('../math');
router.get('/', function(req, res, next) {
        if (req.query.fibonum) {
                math.fiboasync(req.query.fibonum, (err, fiboval) => {
                        if (err) next(err);
                        else {
                                res.render('fibonacci', {
                                        title: 'Calcular numeros fibonacci',
                                        fibonum: req.query.fibonum,
                                        fiboval: math.fibonacci(req.query.fibonum)
                                });
                        }
                });
        } else {
                res.render('fibonacci', {
                        title: 'Calcular numeros fibonacci',
                        fiboval: undefined
                });
        }
});

module.exports = router;
Anuncios

Si lo comparamos con el archivo fibonacci.js notaremos que es muy similar pero aqui al momento de llamar a la funcion que definimos anteriormente tenemos una funcion de callback donde si ocurre un error lo devolveremo mediante el next y sino lo mostraremos mediante render tal como hacemos con el otro archivo del cual hablamos en el post anteiror, nuestro siguiente paso sera comentar la siguiente linea en app.js:

var fibonacciRouter = require('./routes/fibonacci');
Anuncios

Y agregaremos la siguiente linea:

var fibonacciRouter = require('./routes/fibo-async');
Anuncios

En este caso le cambiamos el router de fibonacci al archivo creada aqui, pero no borren el anterior sino simplemente comentelo porque lo usaremos nuevamente, con todo esto comentado veamos como trabaja ahora mediante el siguiente video

Anuncios

En el video vemos como al calcular un numero de fibonacci que toma mas tiempo de lo habitual en una ventana, en otra aplicamos otro mas rapido y tambien vimos que funciona sin ningun inconveniente mientras la otra lo procesa, con esto ahora tenemos una aplicacion optimizada con nuestro servidor, antes de finalizar les dejo un link con todos los archivos trabajados y del proyecto:

Anuncios

En resumen, hoy hemos visto como nos afecta el usoo de un codigo intensivo, en este caso usamos a fibonacci para numeros grandes, primero vimos el inconveniente con otro script, lo solucionamos pero tuvimos que resolverlo mediante funciones asincronicas, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donación

Es para mantenimento del sitio, gracias!

$1.50