miércoles, 13 de enero de 2016

Aplana tu código asíncrono en JavaScript

Debido al modelo asíncrono y de loop de eventos de NodeJS, en ciertas ocasiones nos encontramos con una situación en la que tenemos una terrible pirámide de callbacks y acabamos en lo que la gente llama "callback hell".

Pongamos un ejemplo muy tonto en el que necesito realizar un request http a un site, obtener unos datos y luego escribir esos datos en disco. Estas 2 operaciones son asíncronas por lo que si hacemos algo así:

var response = request.get(Url);
fs.createWriteStream(filePath, response);

Nos vamos a encontrar que response está vacío siempre -o la mayoría de las veces- cuando vamos a escribir en disco. Así que empezamos nuestra pirámide de callbacks de la muerte:

function main(){
    request.get(Url, function(err, response){
        fs.writeFile(filePath, response);
    });
}

Y si necesitamos hacer algo con el fichero después de escribirlo en disco, tendremos que anidar otro callback que sea llamado cuando termine la función writeFile..., y así hasta que ya no puedas leer el código cómodamente y tu vida se convierta en un infierno.

Para solucionar esto, existen eventos en NodeJS y se han creado un buen montón de abstracciones como promesas, async, programación reactiva, etc... Pero si yo estoy haciendo algo de 300 líneas de código ¿De verdad necesito importar una librería de 30.000 líneas? ¿De verdad necesito "eventificar" todas mis funciones asíncronas?¿Qué sentido tiene esto? Alguno podrá decir que si son 300 líneas de código no merece la pena ni plantearse hacer un código legible; pero ya te digo yo a ti que con JavaScript esas 300 líneas pueden ser las más infernales que hayas visto nunca.

Un técnica para acabar con las pirámides de callbacks sin dependencias y sin tener que "eventificar" (que además luego está todo lleno de cadenas mágicas de mierda) es utilizar Generadores para "pausar" el flujo de código según nuestras necesidades.

function*  main(){

    response = yield getData('someURL');//espera otro next() antes de continuar
    writeDataToFile('someFilename', response);  
}

function getData(Url){

    var callback = function(err, response){
                  err != null ? console.log(err); : mainFlow.next(response); //continúa el siguiente paso de la función main retornándole los datos que nos interesan
               };

    request.get(Url. callback);
}

function writeDataToFile(fileName, data){

    fs.writeFile(fileName, data);
}

var mainFlow = main(); //instanciar el generador
mainFlow.next();//comenzar la ejecución


Tadaaaaa!!! Mira que bien se lee la función main() de arriba abajo de forma secuencial, viendo paso por paso lo que se está haciendo. Y los callbacks todos serán iguales en tu arquitectura, o error o next(), pudiendo cambiar el código en la funcion main() sin que el resto del código tenga que saber nada. Independencia de código y bajo acoplamiento, responsabilidad única, inversión de control etc. ¡Nos estamos acercando al paradigma SOLID con JavaScript! ¡Casi no me lo creo!



No hay comentarios:

Publicar un comentario