Programación asincrónica de JavaScript y devoluciones de llamada

JavaScript es síncrono de forma predeterminada y es de un solo subproceso. Esto significa que el código no puede crear nuevos subprocesos y ejecutarse en paralelo. Descubra qué significa el código asincrónico y cómo se ve

Asincronicidad en lenguajes de programación

Las computadoras son asincrónicas por diseño.

Asincrónico significa que las cosas pueden suceder independientemente del flujo del programa principal.

En las computadoras de consumo actuales, cada programa se ejecuta durante un intervalo de tiempo específico y luego detiene su ejecución para permitir que otro programa continúe su ejecución. Esto se ejecuta en un ciclo tan rápido que es imposible notarlo, y creemos que nuestras computadoras ejecutan muchos programas simultáneamente, pero esto es una ilusión (excepto en máquinas multiprocesador).

Programas de uso internointerrumpe, una señal que se emite al procesador para llamar la atención del sistema.

No entraré en los aspectos internos de esto, pero tenga en cuenta que es normal que los programas sean asíncronos y detengan su ejecución hasta que necesiten atención y la computadora pueda ejecutar otras cosas mientras tanto. Cuando un programa está esperando una respuesta de la red, no puede detener el procesador hasta que finalice la solicitud.

Normalmente, los lenguajes de programación son sincrónicos y algunos proporcionan una forma de gestionar la asincronicidad, en el lenguaje o mediante bibliotecas. C, Java, C #, PHP, Go, Ruby, Swift, Python, todos son sincrónicos por defecto. Algunos de ellos manejan async mediante el uso de subprocesos, generando un nuevo proceso.

JavaScript

JavaScript essincrónicode forma predeterminada y es de un solo subproceso. Esto significa que el código no puede crear nuevos subprocesos y ejecutarse en paralelo.

Las líneas de código se ejecutan en serie, una tras otra, por ejemplo:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()

Pero JavaScript nació dentro del navegador, su trabajo principal, al principio, era responder a las acciones del usuario, comoonClick,onMouseOver,onChange,onSubmitetcétera. ¿Cómo podría hacer esto con un modelo de programación síncrona?

La respuesta estaba en su entorno. losnavegadorproporciona una forma de hacerlo al proporcionar un conjunto de API que pueden manejar este tipo de funcionalidad.

Más recientemente, Node.js introdujo un entorno de E / S sin bloqueo para extender este concepto al acceso a archivos, llamadas de red, etc.

Devoluciones de llamada

No puede saber cuándo un usuario va a hacer clic en un botón, así que lo que debe hacer esdefinir un controlador de eventos para el evento de clic. Este controlador de eventos acepta una función, que se llamará cuando se active el evento:

document.getElementById('button').addEventListener('click', () => {
  //item clicked
})

Este es el llamadollamar de vuelta.

Una devolución de llamada es una función simple que se pasa como un valor a otra función y solo se ejecutará cuando ocurra el evento. Podemos hacer esto porque JavaScript tiene funciones de primera clase, que se pueden asignar a variables y pasar a otras funciones (llamadasfunciones de orden superior)

Es común envolver todo el código de su cliente en unloaddetector de eventos en elwindowobjeto, que ejecuta la función de devolución de llamada solo cuando la página está lista:

window.addEventListener('load', () => {
  //window loaded
  //do what you want
})

Las devoluciones de llamada se utilizan en todas partes, no solo en eventos DOM.

Un ejemplo común es el uso de temporizadores:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

Las solicitudes XHR también aceptan una devolución de llamada, en este ejemplo, asignando una función a una propiedad que se llamará cuando ocurra un evento en particular (en este caso, el estado de la solicitud cambia):

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()

Manejo de errores en devoluciones de llamada

¿Cómo maneja los errores con devoluciones de llamada? Una estrategia muy común es usar lo que adoptó Node.js: el primer parámetro en cualquier función de devolución de llamada es el objeto de error:devoluciones de llamada de error primero

Si no hay error, el objeto esnull. Si hay un error, contiene alguna descripción del error y otra información.

fs.readFile('/file.json', (err, data) => {
  if (err !== null) {
    //handle error
    console.log(err)
    return
  }

//no errors, process data console.log(data) })

El problema con las devoluciones de llamada

¡Las devoluciones de llamada son ideales para casos simples!

Sin embargo, cada devolución de llamada agrega un nivel de anidación, y cuando tiene muchas devoluciones de llamada, el código comienza a complicarse muy rápidamente:

window.addEventListener('load', () => {
  document.getElementById('button').addEventListener('click', () => {
    setTimeout(() => {
      items.forEach(item => {
        //your code here
      })
    }, 2000)
  })
})

Este es solo un código simple de 4 niveles, pero he visto muchos más niveles de anidamiento y no es divertido.

¿Cómo resolvemos esto?

Alternativas a las devoluciones de llamada

A partir de ES6, JavaScript introdujo varias características que nos ayudan con el código asincrónico que no implica el uso de devoluciones de llamada:


Más tutoriales de js: