/ design patterns

¿Qué significa que JavaScript es asíncrono?

Seguro que has escuchado muchas veces que debemos recordar que JavaScript es asíncrono, pero ¿Qué es lo que realmente significa y qué problemas puede ocasionarnos?

El primer ejemplo es un request de AJAX usando fetch y si no sabes lo que es esa función, en resumen, es un método para obtener información en base a un URL, este método es una forma más sencilla de hacer requests al tus APIs en comparación al XMLHTTPRequest

fetch(THE_URL)
    .then(data => {
        // todo salió bien y vas a desplegar información
    })
    .catch(_ => {
        console.error('Error while trying to fetch data from the server')
        // desplegar tu error
    })

Notemos que en el snippet anterior, estamos pasando funciones para success y fail (then y catch respectivamente), ¿Por qué decimos que ese código es asíncrono?

Porque el código que pasamos como función será ejecutado hasta que el request HTTP sea resuelto, independientemente que este sea exitoso o no. Para que se vea el riesgo y la "asincronía" de esto, agreguemos un poco más de código

var bookService = {
  /**
   * Retorna los libros leídos por el usuario.
   * @param {String} userId - El id del usuario.
   */
  getBooksReadByUser: function (userId) {
    fetch(API_FOR_USER + '/books')
      .then(data => {
        // TODO: validate if the response header is JSON type
        var dJson = data.json()
      })
      .catch(_ => {
        // TODO: dispatch event to indicate there was an error
        // - for example if using redux
      })
    console.log('este código se ejecuta antes de los callbacks')
    // código que posiblemente debería ejecutarse DESPUES del request
  }
}

¿Han pasado por algo así? Yo si, y fue doloroso no entenderlo a tiempo. Aquí vale la pena hacer un warning: No usen llamados síncronos de HTTP, por ejemplo con XMLHTTPRequest, este bloqueará el hilo principal de ejecución y los usuarios no podrán ni hacer un scroll, y peor aún, si no establecen un timeout para esto, se colgarán hasta que el request sea resuelto. Fetch no cuenta con este feature de síncrono, pero hay otras librerías (por ejemplo: jQuery#ajax) que si, y podrían traerles problemas

En las nuevas versiones de JS, tenemos wrappers bastante útiles para hacer funciones asíncronas y tratarlas como promesas, no vamos a entrar a detalles en este post, pero la idea es:

async function getBooksReadByUser (userId) {
  // await prácticamente le dice, espera a que tengamos esto resuelto
  var books = await ( /* aquí va el request con fetch */)
  return books
}

// en otro lugar..

// tenemos una bonita promesa desde la misma función
// con sólo indicar que la función es asíncrona
getBooksReadByUser('10')
  .then(data => console.log(data))

¿Ven las diferencias? En esta última notación, tenemos la posibilidad de escribir código síncrono y que se ejecute de forma asíncrona haciendo wrappers para establecer que el código será ejecutado de forma lineal, esto para evitar tener problemas y confusiones como las que indicamos originalmente