JavaScript Hell

Como sobrevivir al infierno del JavaScript Asíncrono

JavaScript Hell

¿Por que NodeJS y JS?

  • Excelentes benchmark y reviews
  • Características técnicas interesantes
    • Single Thread y asincrono

  • Experiencia con lenguajes flexibles
  • Popularidad creciente
  • ... Odiaba la sintaxis de Python

JavaScript Hell

Single Threading

  • Lavar ropa
  • Lavar losa
  • Preparar cena
    • Preparar pollo
    • Preparar arroz
    • Preparar bebidas
  • Pedir el domicilio del mercado

Thread

JavaScript Hell

Single Threading

Poner ropa en lavadora

Colgar ropa

Poner losa en lavatrastes

Organizar losa

Servir Arroz

Poner a cocinar arroz

Servir Pollo

Poner pollo en horno

JavaScript Hell

Multi Threading

Poner ropa en lavadora

Colgar ropa

Poner losa en lavatrastes

Organizar losa

Servir Pollo

Poner pollo en horno

JavaScript Hell

Single Threading y Asincronismo

  • Lavar ropa
  • Lavar losa
  • Preparar cena
    • Preparar pollo
    • Preparar arroz
    • Preparar bebidas
  • Pedir el domicilio del mercado

JavaScript Hell

Single Threading y Asincronismo

Poner ropa en lavadora

Colgar ropa

Poner losa en lavatrastes

Organizar losa

Poner pollo en horno

Servir Arroz

Poner a cocinar arroz

Preparar Bebidas

Pedir Domicilio

Servir Pollo

Servir toda la comida

Revisa lista

Recibir Domicilio

Organizar mercado

JavaScript Hell

JavaScript Hell

Single Threading y Asincronismo

  • Alto rendimiento
  • Bajo consumo de recursos
  • Resistente a fallos

JavaScript Hell

Single Threading y Asincronismo

(El problema)

LA PRACTICA ES UNA PESADILLA!!!

JavaScript Hell

JS Asincrono

/**
 * @params ropaSucia La ropa sucia
 * @params {function} cb
 */
function ponerRopaEnLavadora(ropaSucia, cb){

    lavar(ropaSucia, function(err, ropaLimpia){
        if(err){
            return cb(err);
        }
        cb(null, {ropaLimpia:ropaLimpia});
    });

}

JavaScript Hell

JS Asincrono

/**
 * @params ropaSucia
 * @params losaSucia
 * @params insumosCena
 * @params telefonoDomiciliario
 * @params {function} cb
 */
function realizarTareas(ropaSucia, losaSucia, insumosCena, telefonoDomiciliario, cb){

    let resultado = {};

    ponerRopaEnLavadora(ropaSucia, function(err, ropaLimpia){
        if(err){
            return cb(err);
        }
        colgarRopa(ropaLimpia, function(err, ropaSeca){
            if(err){
                return cb(err);
            }
            resultado.ropaSeca = ropaSeca;

            ponerLosaEnLavatrastes(losaSucia, function(err, losaLimpia){
                if(err){
                    return cb(err);
                }
                organizarLosa(losaLimpia, function(err){
                    if(err){
                        return cb(err);
                    }                    

                    prepararPollo(insumosCena.pollo, function(err, pollo){
                        if(err){
                           return cb(err);
                        }
                        let insumosPreparados = {};
                        insumosPreparados.pollo = pollo;
                
                        prepararArroz(insumosCena.arroz, function(err, arrozFrito){
                            if(err){
                               return cb(err);
                            }
                            insumosPreparados.arroz = arrozFrito;
                    
                            prepararBebidas(insumosCena.mora, function(err, jugoDeMora){
                                if(err){
                                   return cb(err);
                                }
                                insumosPreparados.jugo = jugoDeMora;
                                resultado.comidaPreparada = insumosPreparados;
                
                                pedirDomicilio(telefonoDomiciliario, function(err, mercado){
                                    if(err){
                                        return cb(err);
                                    }
                                    cb(null, resultado);
                                });
                            });
                        });
                    });
                });
            });

        });
    });

}

JavaScript Hell

Callback Hell

JavaScript Hell

Callback Hell

/**
 * @params ropaSucia
 * @params losaSucia
 * @params insumosCena
 * @params telefonoDomiciliario
 * @params {function} cb
 */
function realizarTareas(ropaSucia, losaSucia, insumosCena, telefonoDomiciliario, cb){

    let resultado = {};
    let operacionesPendientes = 4;

    function verificarFinal(){
        if(--operacionesPendientes > 0) {
            return;
        }
        cb(null, resultado);
    }

    ponerRopaEnLavadora(ropaSucia, function(err, ropaLimpia){
        if(err){
            return cb(err);
        }
        colgarRopa(ropaLimpia, function(err, ropaSeca){
            if(err){
                return cb(err);
            }
            resultado.ropaSeca = ropaSeca;
            verificarFinal();
        });
    });


    ponerLosaEnLavatrastes(losaSucia, function(err, losaLimpia){
        if(err){
            return cb(err);
        }
        organizarLosa(losaLimpia, function(err){
            if(err){
                return cb(err);
            }
            verificarFinal();
        });
    });

    let cantInsumos = 3, insumosPreparados={};
    function verificarServirCena(err, insumoPreparado, tipoInsumo){
        if(err){
            cb(err);
        }
        
        insumosPreparados[tipoInsumo] = insumoPreparado;
        if(--cantInsumos > 0){
            return;
        }
        resultado.comidaPreparada = insumosPreparados;
        verificarFinal();
    }

    prepararPollo(insumosCena.pollo, function(err, pollo){
        verificarServirCena(err, pollo, "pollo");
    });

    prepararArroz(insumosCena.arroz, function(err, arrozFrito){
        verificarServirCena(err, arrozFrito, "arroz");
    });

    prepararBebidas(insumosCena.mora, function(err, jugoDeMora){
        verificarServirCena(err, jugoDeMora, "jugo");
    });

    pedirDomicilio(telefonoDomiciliario, function(err, mercado){
        if(err){
            return cb(err);
        }
        verificarFinal();
    });
}

JavaScript Hell

caolan / Async

const async = require("async");

...

function lavarRopa(ropaSucia, cb){
    async.waterfall([
        function(cb){
            ponerRopaEnLavadora(ropaSucia, cb);
        },
        function(ropaLimpia, cb){
            colgarRopa(ropaLimpia, cb);
        }
    ], function(err, ropaSeca){
        if(err) { 
            // do something
            return cb(err);
        }
        cb(null, ropaSeca);
    });    
}

... // lavarLosa

function prepararCena(insumosCena, cb){
    async.parallel({
        pollo: function(cb){
             prepararPollo(insumosCena.pollo, cb);   
        },
        arrozFrito: function(cb){
            prepararArroz(insumosCena.arroz, cb);   
        },
        jugoDeMora: function(cb){
            prepararBebidas(insumosCena.mora, cb);   
        }
    }, function(err, insumosPreparados){
        /*
         *   insumosPreparados = {
         *       pollo: new PolloHorneado(), 
         *       arrozFrito: new Arroz(), 
         *       jugoDeMora: new Jugo()
         *  }
         */
        if(err) { 
            // do something
            return cb(err);
        }
        cb(null, insumosPreparados);
    });
}


/**
 * @params ropaSucia
 * @params losaSucia
 * @params insumosCena
 * @params telefonoDomiciliario
 * @params {function} cb
 */
function realizarTareas(ropaSucia, losaSucia, insumosCena, telefonoDomiciliario, cb){

    async.parallel({
        ropaSeca: function(cb){
            lavarRopa(ropaSucia, cb);            
        },
        lavarLosa: function(cb){
            lavarLosa(losaSucia, cb);    
        },
        comidaPreparada: function(cb){
            prepararCena(insumosCena, cb);
        },
        mercado: function(cb){
            pedirDomicilio(telefonoDomiciliario, cb);      
        }
    }, function(err, result){
        if(err) { 
            // do something
            return cb(err);
        }
        cb(null, result);
    })
}

JavaScript Hell

Promises (Promesas)

function lavarRopa(ropaSucia){
    return ponerRopaEnLavadora(ropaSucia)
              .then(function(ropaLimpia){
                 // esto se puede simplificar
                 return colgarRopa(ropaLimpia);
              })
              .then(function(err, ropaSeca){ //Este then sobra
                 return ropaSeca;
              });    
}

... // lavarLosa

function prepararCena(insumosCena){
    return Promise.all([
            prepararPollo(insumosCena.pollo),
            prepararArroz(insumosCena.arroz),
            prepararBebidas(insumosCena.mora)
        ]).then(function(results){ // Array
            const insumosPreparados = {
                pollo: results[0], 
                arrozFrito: results[1], 
                jugoDeMora: results[2]
            }
            return insumosPreparados;
        });
}


/**
 * @params ropaSucia
 * @params losaSucia
 * @params insumosCena
 * @params telefonoDomiciliario
 */
function realizarTareas(ropaSucia, losaSucia, insumosCena, telefonoDomiciliario){
    return Promise.all([
        lavarRopa(ropaSucia),
        lavarLosa(losaSucia),    
        prepararCena(insumosCena),
        pedirDomicilio(telefonoDomiciliario),      
    }).then(function(results){
        results = {
            ropaSeca: results[0],
            lavarLosa: results[1],
            comidaPreparada: results[2],
            mercado: results[3],
        }

        return results;
    })
    .catch(function(err){
        //do somenthing
        console.err(err);
    });
}

JavaScript Hell

async / await (ES7)

async function lavarRopa(ropaSucia){
    const ropaLimpia = await ponerRopaEnLavadora(ropaSucia);
    const ropaSeca = await colgarRopa(ropaLimpia);
    return ropaSeca;
    // return await colgarRopa(await ponerRopaEnLavadora(ropaSucia));
}

... // lavarLosa

async function prepararCena(insumosCena){
    let insumosPreparados = await Promise.all([
        prepararPollo(insumosCena.pollo),
        prepararArroz(insumosCena.arroz),
        prepararBebidas(insumosCena.mora)
    ]);
    return {
        pollo: results[0], 
        arrozFrito: results[1], 
        jugoDeMora: results[2]
    }
}


/**
 * @params ropaSucia
 * @params losaSucia
 * @params insumosCena
 * @params telefonoDomiciliario
 */
async function realizarTareas(ropaSucia, losaSucia, insumosCena, telefonoDomiciliario){
    try{
        let results = await Promise.all([
            lavarRopa(ropaSucia),
            lavarLosa(losaSucia),    
            prepararCena(insumosCena),
            pedirDomicilio(telefonoDomiciliario),      
        });
        return {
            ropaSeca: results[0],
            lavarLosa: results[1],
            comidaPreparada: results[2],
            mercado: results[3],
        }
    } catch(err) {
        //do somenthing
        console.err(err);
    }
}

JavaScript Hell

async / await (ES7): Soporte

JavaScript Hell

Epilogo

JavaScript Hell

Como sobrevivir al infierno del JavaScript Asíncrono

JavaScript Hell

By CucutaJS

JavaScript Hell

  • 85