none
¿Cuáles son las desventajas de utilizar programación asíncrona? RRS feed

  • Pregunta

  • Buenas, he escuchado algunas desventajas de utilizar programación asíncrona pero la principal es que no se sabe cómo dar continuidad a las operaciones no bloqueantes del algoritmo una vez que éstas han terminado su ejecución, pero no lo entiendo del todo, además que he escuchado de personas que lo utilizan en base de datos y me gustaría saber su experiencia a la hora de hacer peticiones, ¿lo recomiendan o han tenido algún tipo de problema?

    Los otros problemas que encontré son sobre la desventaja de no poder utilizar el try/catch común y que cuando se utiliza en métodos (métodos asíncronos), todos los métodos que se manden llamar dentro de este método deben de ser también asíncronos.

    Saludos y de antemano muchas gracias. 
    • Editado erickgtzh viernes, 17 de mayo de 2019 18:20
    viernes, 17 de mayo de 2019 18:19

Respuestas

  • Lenguajes asíncronos

    Su depuración no es tan sencilla. Un código puede que compile correctamente y puede que no funcione como lo pensamos. Puede que tengamos una función que depende de otra se ejecuta cuando no lo teníamos pensado.

    Excelentes para el manejo de interfaces.
    Se podría decir que son más dificiles de «entender», debido a que es otro paradigma de programación.
    Existen diversas infraestructuras (frameworks) que permiten a varios lenguajes (Python,Java,PHP, entre otros) convertirse en «lenguajes asíncronos». El analísis de cada framework va mucho más alla de las metas de esta publicación.

    Comparaciones entre los paradigmas
    Para esto utilizaremos un lenguaje sencillo de entender como PHP para analizar los lenguajes síncronos, y NodeJS (este último es un framework de Javascript) para los lenguajes asíncronos.

    module.exports = function(app){
      var async = require('async');
      var fn = require('./functions.js');
     
      app.get('/', (req,res) => {
        var usuario,data,menu,header,body,footer;
          async.series([
            function loadUser(callback){
              fn.cargarUsuario(req.session.user,(err,data)=>{
                usuario = data;
                callback();
              });
     
            },
            function cargarBaseDatos(callback){
              fn.cargarDatosBaseDatos(usuario, (err,db)=>{
                data = db;
                callback();
              });
            },
            function cargarDatosMenu(callback){
              fn.cargarDatosMenu(usuario, (err,data)=> {
                menu = data;
                callback();
              });
            },
            function cargarHeader(callback){
              fn.cargarHeader( (err,data)=>{
                header=data;
                callback();
              });
            },
            function cargarCuerpo(callback){
              fn.cargarCuerpo(data,menu,usuario, (err,bd)=>{
               body = bd;
               callback();
             });
            },
            function cargarFooter(callback){
              fn.cargarFooter(menu,usuario, (err,data)=>{
                footer = data;
                callback();
              });
            }
          ],
            (err)=>{
              var variables = {header:header,body:body,footer:footer};
              req.render('ejemplo.ejs',variables);
          });
      });
    }
    <?php
     
      include('conexion_base_datos.php');
      include('funciones.php');
     
      $usuario = cargarDatosUsuario($_SESSION['user']);
      $data = cargarDatosBaseDatos($usuario);
      $menu = cargarDatosDelMenu($usuario);
      $header  = cargarHeader();
      $body = cargarCuerpo($data,$menu,$usuario);
      $footer = cargarFooter($menu,$usuario);
     
      crearPagina($header,$body,$footer);
     
     ?>

    Comparación de códigos.

    A la izquierda Javascript y a la derecha PHP Ambos códigos intentan resolver el mismo problema: mostrar una página web en donde se requiera cargar información del usuario. Es necesario cargar información de una base de datos (de cualquier cosa, ya sea productos, inventario, etc), cargar la cabecera de la página, el cuerpo y el pie de página y renderizar la misma. Se han obviado las comprobaciones de errores para hacer el código más corto y sencillo de entender.

    PHP

    Como podemos apreciar, PHP es mucho más fácil de leer. Sabemos que todas las funciones que estamos llamando estan dentro de un archivo de funciones llamado «funciones.php», y que cada función se va a ejecutar justo después que se ejecute la anterior.

    Javascript

    En el ejemplo de Javascript, se utilizó una librería o módulo llamado «async». Este permite agrupar funciones y hacer que el código sea más limpio. En este algoritmo se espera a que cada función termine de ejecutarse para iniciar la siguiente. La diferencia esta en que debido a la naturaleza asincrona de NodeJS (un framework de Javascript) debemos utilizar callbacks, las cuales son llamadas que se realizan cuando el código de una función termina de ejecutarse. Es una forma de hacerle saber al código o función principal que la función secundaria ha acabado de ejecutarse.

    Es decir, en NodeJS no se puede hacer esto:

    app.get('/', function(req,res){
      var usuario = fn.cargarUsuario();
      var data = fn.cargarDatosBaseDatos(usuario);
      var menu = fn.cargarDatosMenu(usuario);
      var header = fn.cargarHeader();
      var body = fn.cargarCuerpo(data,menu,usuario);
      var footer = fn.cargarFooter(menu,usuario);
      var variables = {header:header,body:body,footer:footer};
      req.render('ejemplo.ejs',variables);
    })

    El código anterior compilaría correctamente, pero puede dar resultados no esperados. Es decir, el programa llama a la primera función e inmediatamente llama a la segunda, y así se va. Nunca espera a que la función anterior se termine de ejecutar. Entonces como la variable data es la que contiene la información de la base de datos que nos interesa, puede que no este lista cuando se llame a las funciones que dependan de esta ( en nuestro código sería cargarCuerpo() ). Esto podría ocasionar que el cuerpo de la página quede vacío.

    Callbacks

    Para solucionar el problema anterior se implementa el concepto de callbacks, para poder saber cuando una función termina de ejecutarse. Esto es muy útil, debido a es posible que la función que carga la base de datos tarde mucho. En otros lenguajes de programación esto consume recursos del sistema. Los lenguajes asincronos solo se consume los recursos por parte del servicio que utiliza la base de datos.

    La sintaxis de un callback es sencilla, y una función normal puede ser transformada a una función asincrona. Para Javascript:

    Como podemos observar, cuando se utilizan callbacks se suele adoptar la notación de «Error primero» (o error first en ingles). Es decir, un callback deberá devolver dos valores:

    callback(err,data)

    Esto es una norma, y se hace para atrapar errores. Lo que usualmente se suele hacer es devolver nulo la variable err si no ha ocurrido ningún error, y lo contrario si ha ocurrido.

    Promesas

    Las promesas representan un valor o un objeto que puede estar disponible ahora o en cualquier momento futuro. Es decir, no se sabe en que momento puedan ser resueltas y ni cuando su valor de retorno estará disponible. El concepto es similar al de un callback, pero su sintaxis es un poco diferente.

    Una promesa tiene dos formas de regresar al código original: si se completa o si ocurre un error. Esto es fácil de controlar utilizando los parámetros fullfill y reject. Éstos son son utilizados en el siguiente código:

    return new Promise(function(fullfill,reject){
        //Codigo
        //Si se desea completar, se utiliza fullfill(valorAretornar);
        //Si se desea mandar un error, se utiliza reject("error!");
    });
    1
    2
    3
    4
    5
    return new Promise(function(fullfill,reject){
        //Codigo
        //Si se desea completar, se utiliza fullfill(valorAretornar);
        //Si se desea mandar un error, se utiliza reject("error!");
    });

    Lo anterior se puede convertir una función que este basada en callbacks a una con promesas, de la siguiente forma:

    La ventaja de las promesas es que se pueden «combinar». Es decir, si tenemos una función que cargue datos de la base de datos y necesitamos esos datos para procesarlos, podemos hacer algo como esto (asumiendo que la función cargarDatos() carga los datos de una base de datos, y que la función analizarDatos() los analiza de la manera que queremos) :

    fn.cargarDatos(usuario).then(function (datos) {
      //La variable datos contiene lo que se devuelve de
      //la promesa "cargarDatos."
    });

    Y podemos seguir combinándola para utilizar más funciones

    fn.cargarDatos(usuario).then(function(datos){
      return analizarDatos(datos);
    }).then(function (datosAnalizados){
      //Aqui tenemos los datos ya analizados, en la variable datosAnalizados
    });

    Este código anterior también se puede escribir de la siguiente forma (asumiendo que analizarDatos es una promesa):

    fn.cargarDatos(usuario).then(analizarDatos).then(function(datosAnalizados){
      //Aqui tenemos los datos ya analizados, en la variable datosAnalizados
    });

    Esto ahorra bastante espacio en comparación con los callbacks.

    Estilos de ejecuciones asíncronas

    Serie: Se ejecuta una promesa o una función asíncrona (basada en callbacks) después que se ejecuta la anterior.

    Paralelo: Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo.

    Paralelo con límite de ejecuciones paralelas: Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo, pero con un límite de procesos simultáneos.

    Carrera (race): Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo, y cuando uno de estos se termina de ejecutar entonces se regresa al código original y se dejan de ejecutar los demás procesos asíncronos iniciados.
    Esta información será útil para las siguientes publicaciones de Javascript y de interfaces web.

    Gracias por usar los foros de MSDN.

    Carlos Ruiz
     ____

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde. 

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.  

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft.
    viernes, 17 de mayo de 2019 18:52
  • hola

    >>he escuchado algunas desventajas de utilizar programación asíncrona

    mmm depnde de donde lo hayas escuchado

    porque quizas depende si esto tendra algun beneficio segun lo que se quiera conseguir

    Por ejemplo, en desarrollo web es MUUUUY importante ya que como quizas conozcas el server web usa un pool de thread para procesar los request y no bloquearlos permite que un thread atienda a mas de un request del cliente aumentando la performance

    >>pero la principal es que no se sabe cómo dar continuidad a las operaciones no bloqueantes del algoritmo una vez que éstas han terminado su ejecución

    si usas async\await esto es muy simple

    Modelo de programación asincrónica de tareas en C#

    >>no poder utilizar el try/catch común y que cuando se utiliza en métodos (métodos asíncronos), todos los métodos que se manden llamar dentro de este método deben de ser también asíncronos

    nuevamente depende que quieras lograr, cada thread que lances se supone es un proceso independientes, puedes controlar los errores dentro de ese proceso y generar una respuesta o puedes controlar los errores globalmente

    Threading in C#: 7 Things you should always remember about

    analiza lo que dice el punto 5

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    viernes, 17 de mayo de 2019 20:10

Todas las respuestas

  • Hola Eric ! Mirá su depuración no es fácil, puede ser que se compile un código y no funcione como vos pensaste, es mas dificil de "comprender" ya que es otro paradigma de programación.
    viernes, 17 de mayo de 2019 18:31
  • Lenguajes asíncronos

    Su depuración no es tan sencilla. Un código puede que compile correctamente y puede que no funcione como lo pensamos. Puede que tengamos una función que depende de otra se ejecuta cuando no lo teníamos pensado.

    Excelentes para el manejo de interfaces.
    Se podría decir que son más dificiles de «entender», debido a que es otro paradigma de programación.
    Existen diversas infraestructuras (frameworks) que permiten a varios lenguajes (Python,Java,PHP, entre otros) convertirse en «lenguajes asíncronos». El analísis de cada framework va mucho más alla de las metas de esta publicación.

    Comparaciones entre los paradigmas
    Para esto utilizaremos un lenguaje sencillo de entender como PHP para analizar los lenguajes síncronos, y NodeJS (este último es un framework de Javascript) para los lenguajes asíncronos.

    module.exports = function(app){
      var async = require('async');
      var fn = require('./functions.js');
     
      app.get('/', (req,res) => {
        var usuario,data,menu,header,body,footer;
          async.series([
            function loadUser(callback){
              fn.cargarUsuario(req.session.user,(err,data)=>{
                usuario = data;
                callback();
              });
     
            },
            function cargarBaseDatos(callback){
              fn.cargarDatosBaseDatos(usuario, (err,db)=>{
                data = db;
                callback();
              });
            },
            function cargarDatosMenu(callback){
              fn.cargarDatosMenu(usuario, (err,data)=> {
                menu = data;
                callback();
              });
            },
            function cargarHeader(callback){
              fn.cargarHeader( (err,data)=>{
                header=data;
                callback();
              });
            },
            function cargarCuerpo(callback){
              fn.cargarCuerpo(data,menu,usuario, (err,bd)=>{
               body = bd;
               callback();
             });
            },
            function cargarFooter(callback){
              fn.cargarFooter(menu,usuario, (err,data)=>{
                footer = data;
                callback();
              });
            }
          ],
            (err)=>{
              var variables = {header:header,body:body,footer:footer};
              req.render('ejemplo.ejs',variables);
          });
      });
    }
    <?php
     
      include('conexion_base_datos.php');
      include('funciones.php');
     
      $usuario = cargarDatosUsuario($_SESSION['user']);
      $data = cargarDatosBaseDatos($usuario);
      $menu = cargarDatosDelMenu($usuario);
      $header  = cargarHeader();
      $body = cargarCuerpo($data,$menu,$usuario);
      $footer = cargarFooter($menu,$usuario);
     
      crearPagina($header,$body,$footer);
     
     ?>

    Comparación de códigos.

    A la izquierda Javascript y a la derecha PHP Ambos códigos intentan resolver el mismo problema: mostrar una página web en donde se requiera cargar información del usuario. Es necesario cargar información de una base de datos (de cualquier cosa, ya sea productos, inventario, etc), cargar la cabecera de la página, el cuerpo y el pie de página y renderizar la misma. Se han obviado las comprobaciones de errores para hacer el código más corto y sencillo de entender.

    PHP

    Como podemos apreciar, PHP es mucho más fácil de leer. Sabemos que todas las funciones que estamos llamando estan dentro de un archivo de funciones llamado «funciones.php», y que cada función se va a ejecutar justo después que se ejecute la anterior.

    Javascript

    En el ejemplo de Javascript, se utilizó una librería o módulo llamado «async». Este permite agrupar funciones y hacer que el código sea más limpio. En este algoritmo se espera a que cada función termine de ejecutarse para iniciar la siguiente. La diferencia esta en que debido a la naturaleza asincrona de NodeJS (un framework de Javascript) debemos utilizar callbacks, las cuales son llamadas que se realizan cuando el código de una función termina de ejecutarse. Es una forma de hacerle saber al código o función principal que la función secundaria ha acabado de ejecutarse.

    Es decir, en NodeJS no se puede hacer esto:

    app.get('/', function(req,res){
      var usuario = fn.cargarUsuario();
      var data = fn.cargarDatosBaseDatos(usuario);
      var menu = fn.cargarDatosMenu(usuario);
      var header = fn.cargarHeader();
      var body = fn.cargarCuerpo(data,menu,usuario);
      var footer = fn.cargarFooter(menu,usuario);
      var variables = {header:header,body:body,footer:footer};
      req.render('ejemplo.ejs',variables);
    })

    El código anterior compilaría correctamente, pero puede dar resultados no esperados. Es decir, el programa llama a la primera función e inmediatamente llama a la segunda, y así se va. Nunca espera a que la función anterior se termine de ejecutar. Entonces como la variable data es la que contiene la información de la base de datos que nos interesa, puede que no este lista cuando se llame a las funciones que dependan de esta ( en nuestro código sería cargarCuerpo() ). Esto podría ocasionar que el cuerpo de la página quede vacío.

    Callbacks

    Para solucionar el problema anterior se implementa el concepto de callbacks, para poder saber cuando una función termina de ejecutarse. Esto es muy útil, debido a es posible que la función que carga la base de datos tarde mucho. En otros lenguajes de programación esto consume recursos del sistema. Los lenguajes asincronos solo se consume los recursos por parte del servicio que utiliza la base de datos.

    La sintaxis de un callback es sencilla, y una función normal puede ser transformada a una función asincrona. Para Javascript:

    Como podemos observar, cuando se utilizan callbacks se suele adoptar la notación de «Error primero» (o error first en ingles). Es decir, un callback deberá devolver dos valores:

    callback(err,data)

    Esto es una norma, y se hace para atrapar errores. Lo que usualmente se suele hacer es devolver nulo la variable err si no ha ocurrido ningún error, y lo contrario si ha ocurrido.

    Promesas

    Las promesas representan un valor o un objeto que puede estar disponible ahora o en cualquier momento futuro. Es decir, no se sabe en que momento puedan ser resueltas y ni cuando su valor de retorno estará disponible. El concepto es similar al de un callback, pero su sintaxis es un poco diferente.

    Una promesa tiene dos formas de regresar al código original: si se completa o si ocurre un error. Esto es fácil de controlar utilizando los parámetros fullfill y reject. Éstos son son utilizados en el siguiente código:

    return new Promise(function(fullfill,reject){
        //Codigo
        //Si se desea completar, se utiliza fullfill(valorAretornar);
        //Si se desea mandar un error, se utiliza reject("error!");
    });
    1
    2
    3
    4
    5
    return new Promise(function(fullfill,reject){
        //Codigo
        //Si se desea completar, se utiliza fullfill(valorAretornar);
        //Si se desea mandar un error, se utiliza reject("error!");
    });

    Lo anterior se puede convertir una función que este basada en callbacks a una con promesas, de la siguiente forma:

    La ventaja de las promesas es que se pueden «combinar». Es decir, si tenemos una función que cargue datos de la base de datos y necesitamos esos datos para procesarlos, podemos hacer algo como esto (asumiendo que la función cargarDatos() carga los datos de una base de datos, y que la función analizarDatos() los analiza de la manera que queremos) :

    fn.cargarDatos(usuario).then(function (datos) {
      //La variable datos contiene lo que se devuelve de
      //la promesa "cargarDatos."
    });

    Y podemos seguir combinándola para utilizar más funciones

    fn.cargarDatos(usuario).then(function(datos){
      return analizarDatos(datos);
    }).then(function (datosAnalizados){
      //Aqui tenemos los datos ya analizados, en la variable datosAnalizados
    });

    Este código anterior también se puede escribir de la siguiente forma (asumiendo que analizarDatos es una promesa):

    fn.cargarDatos(usuario).then(analizarDatos).then(function(datosAnalizados){
      //Aqui tenemos los datos ya analizados, en la variable datosAnalizados
    });

    Esto ahorra bastante espacio en comparación con los callbacks.

    Estilos de ejecuciones asíncronas

    Serie: Se ejecuta una promesa o una función asíncrona (basada en callbacks) después que se ejecuta la anterior.

    Paralelo: Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo.

    Paralelo con límite de ejecuciones paralelas: Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo, pero con un límite de procesos simultáneos.

    Carrera (race): Todos los procesos asíncronos (promesas o función asíncrona basada en callbacks) se ejecutan al mismo tiempo, y cuando uno de estos se termina de ejecutar entonces se regresa al código original y se dejan de ejecutar los demás procesos asíncronos iniciados.
    Esta información será útil para las siguientes publicaciones de Javascript y de interfaces web.

    Gracias por usar los foros de MSDN.

    Carlos Ruiz
     ____

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde. 

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.  

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft.
    viernes, 17 de mayo de 2019 18:52
  • hola

    >>he escuchado algunas desventajas de utilizar programación asíncrona

    mmm depnde de donde lo hayas escuchado

    porque quizas depende si esto tendra algun beneficio segun lo que se quiera conseguir

    Por ejemplo, en desarrollo web es MUUUUY importante ya que como quizas conozcas el server web usa un pool de thread para procesar los request y no bloquearlos permite que un thread atienda a mas de un request del cliente aumentando la performance

    >>pero la principal es que no se sabe cómo dar continuidad a las operaciones no bloqueantes del algoritmo una vez que éstas han terminado su ejecución

    si usas async\await esto es muy simple

    Modelo de programación asincrónica de tareas en C#

    >>no poder utilizar el try/catch común y que cuando se utiliza en métodos (métodos asíncronos), todos los métodos que se manden llamar dentro de este método deben de ser también asíncronos

    nuevamente depende que quieras lograr, cada thread que lances se supone es un proceso independientes, puedes controlar los errores dentro de ese proceso y generar una respuesta o puedes controlar los errores globalmente

    Threading in C#: 7 Things you should always remember about

    analiza lo que dice el punto 5

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    viernes, 17 de mayo de 2019 20:10