Articulos
Contacto

JavaScript - ES6. Bucles Asíncronos

Desarrollo bucles asíncronos ES6 - EcmaScript 2015

Publicado el: 30 de Marzo 2019 por Victor de Andrés Archivado en:
ES6 - Bucles Asincronos
Photo by Olia Nayda Images on Unsplash

Como ya sabes una de las grandes ventajas de Javascript es que es una programación asíncrona. Pero al igual que es una gran ventaja también puede ser una fuente muy común de errores y confusiones para todo tipo de programadores. En este artículo vamos a ver como poder hacer bucles asíncronos.

En esta ocasión daré por entendido que conoces el funcionamiento de las promesas en Javascript, si no puedes ver el siguiente link de mdn, donde se explica el funcionamiento de las mismas.

¿ Cuál es mi error ?. No funciona la promesa.

Si ejecutamos el siguiente código:

      
    'use strict';

    function showValue(item) {
    
      const self = item;
    
      return new Promise ( resolve => {
        setTimeout( () => {
          console.info(self);
          resolve(); 
        }, Math.floor((Math.random() * 10) + 3) * 1000);  // Random 1 - 3 segundos.
      })
    }
    
    async function processArray(array) {
      array.forEach(async (item) => {
        await showValue(item);
      })
      console.log('Fin');
    }
    
    processArray([1, 2, 3, 4, 5]);
        
      
    

Esperaríamos el siguiente resultado por pantalla:

      
    1
    2
    3
    4
    5
    Fin    
      
    

Pero obtendremos algo similar a lo siguiente:

      
    Fin
    4
    2
    1
    3
    5
      
    

Este es un posible resultado ya que estamos haciendo una pausa aleatoria de entre 1 y 3 segundos cuando ejecutamos la función por lo que el resultado puede variar de una ocasión a otra.

Pero porque es esto así.

Si modificamos nuestra código de ejemplo, y añadimos un nueva línea en el momento que comienza la ejecución de la función lo veremos un poco más claro.

El código de la función sería como sigue:

        
    function showValue(item) {

    const self = item;
    
    console.info('Iteración: ', self);
    
    return new Promise ( resolve => {
      setTimeout( () => {
        console.info(self);
        resolve(); 
      }, Math.floor((Math.random() * 10) + 3) * 1000);  // Random 1 - 3 segundos.
    })
    }
      
    

Y en esta ocasión el resultado que obtendremos es el siguiente:

      
    Iteración: 1
    Iteración: 2
    Iteración: 3
    Iteración: 4
    Iteración: 5
    Fin
    4
    1
    2
    3
    5
      
    

Como puedes observar las iteraciones han comenzado correctamente, y una vez lanzadas todas las iteraciones se ejecuta el fin de nuestras función. Pero nosotros en realidad lo que deseamos es que no se ejecute la siguiente iteración hasta haber finalizado la anterior.

Procesamiento secuencial.

Para solucionar nuestro problema de una forma elegante y sencilla podemos hacer uso de la sentencia for … of que nos permite una ejecución secuencial dentro de nuestro bucle. Está es una nueva caracteristica que disponemos desde ES6 o EcmaScript 2015

Quedando el código de nuestro loop de la siguiente forma:

      
    async function processArray(array) {
    for ( let item of array ) {
      await showValue(item);
    }
    console.log('Fin');
    }
      
    

Si ejecutamos nuestro nuevo código obtendremos el resultado que deseamos.

      
    1
    2
    3
    4
    5
    Fin    
      
    

Procesamiento paralelo.

Otra posible solución para nuestro problema será el procesamiento en paralelo de todas nuestras funciones/promesas. Este método es más rápido que el anterior ya que se ejecutan todas las funciones en paralelo, tenlo en cuenta porque arrays grandes pueden generar una sobrecarga de la memoria o la CPU.

Nuestro nuevo código sería como el siguiente:

      
    async function processArray(array) {
      const promises = array.map( (item) => {
        return showValue(item);
      });
      await Promise.all(promises);
      console.log('Fin');
    }    
      
    

En esta ocasión lo que hemos creado es un array con las llamadas a nuestra función asíncrona, y una vez creado el array lo hemos utilizado para llamar a nuestras funciones de forma paralela con la sentencia Promise.all la cual no nos devuelve una respuesta hasta que existe un fallo, o se ejecutan todas las promesas.

Recuerda que en la resolución de nuestra promesa hemos utilizado un random de tiempo por lo que la respuesta obtenida puede ser como la siguiente:

      
      2
      1
      5
      4
      3
      Fin    
      
    

Si eliminamos el random tiempo de nuestra función de respuesta obtendremos el siguiente resultado:

      
    1
    2
    3
    4
    5
    Fin    
      
    

Conclusión.

Por su puesto es sólo son dos soluciones de las muchas que hay, ya que el este problema se puede plantear de distintas formas. Pero personalmente creo ambas soluciones son bastante sencillas y elegantes, sólo debemos tener claro cuál de ellas se adapta mejor a nuestro problema y aplicarla.

Comentarios
Escribe tu comentario