Programación Asíncrona en JavaScript: Guía Completa de async/await

¿Qué es la Programación Asíncrona en JavaScript?

  Programación Asíncrona en JavaScript

La programación asíncrona es un enfoque en la programación que permite ejecutar operaciones sin bloquear la ejecución del código. Esto es especialmente útil en JavaScript, donde las operaciones como la manipulación del DOM y las solicitudes de red pueden llevar tiempo.

A continuación, se explican algunos conceptos clave en la programación asíncrona.

Callbacks

Un callback es una función que se pasa como argumento a otra función y se ejecuta después de que se completa la operación. Los callbacks son una forma de manejar la asincronía en JavaScript. En este escenario, la función obtenerDatos recibe una URL y una función de callback. Una vez que se obtienen los datos, se llama al callback con los datos obtenidos.

Ejemplo de Callbacks
javascript
function obtenerDatos(url, callback) {
  fetch(url)
      .then(response => response.json())
      .then(data => callback(data))
      .catch(error => console.error('Error:', error));
}

obtenerDatos('https://api.example.com/data', (data) => {
  console.log('Datos recibidos:', data);
});

Promises

Una promesa es un objeto que representa la eventual finalización (o falla) de una operación asincrónica y su valor resultante. Las promesas pueden estar en uno de tres estados: pendiente, cumplida o rechazada.

Crear y manejar promesas

Las promesas se crean usando el constructor Promise, y se manejan utilizando los métodos .then() y .catch()

javascript
const promesa = new Promise((resolve, reject) => {
  const exito = true; // Cambiar a false para simular un error
  if (exito) {
      resolve("Operación exitosa!");
  } else {
      reject("Algo salió mal.");
  }
});

promesa
  .then(resultado => console.log(resultado))  // Maneja el caso de éxito
  .catch(error => console.error(error));      // Maneja el caso de error

Usando Promises con fetch

Las promesas son especialmente útiles con la API fetch, que permite realizar solicitudes HTTP.

javascript
fetch('https://api.example.com/data')
  .then(response => {
      if (!response.ok) {
          throw new Error('Error en la red');
      }
      return response.json();
  })
  .then(data => console.log('Datos recibidos:', data))
  .catch(error => console.error('Error:', error));

¿Qué es async/await en JavaScript?

async/await es una sintaxis introducida en ES2017 (ES8) que permite trabajar con Promesas de una forma más legible y sin necesidad de usar múltiples callbacks o .then().

  • async: Marca una función como asíncrona, lo que significa que siempre devolverá una Promesa.
  • await: Hace que JavaScript espere la resolución de una Promesa dentro de una función async.

Las palabras clave async/await permiten trabajar con promesas de manera más sencilla y legible. La palabra clave async se utiliza para declarar una función que siempre devolverá una promesa. La palabra clave await se usa para esperar el resultado de una promesa.

En este contexto, la función obtenerDatosAsync utiliza await para esperar la resolución de la promesa devuelta por fetch. Esto hace que el código sea más fácil de leer y entender.

javascript
async function obtenerDatosAsync() {
      let url = 'https://api.example.com/data';

      try {
          const response = await fetch(url);
          if (!response.ok) {
              throw new Error('Error en la red');
          }
          const data = await response.json();
          console.log('Datos recibidos:', data);
      } catch (error) {
          console.error('Error:', error);
      }
  }
  // Llamando a la función
  obtenerDatosAsync();
  Resumen

  • Callbacks: son funciones pasadas como argumentos a otras funciones y se ejecutan al finalizar una operación.
  • Promises: son objetos que representan la eventual finalización de una operación asíncrona y permiten manejar errores de manera más efectiva.
  • async/await: simplifica el trabajo con promesas, permitiendo escribir código asíncrono de una manera más parecida al código sincrónico.

A continuación, presento algunos ejemplos prácticos para ilustrar de manera más clara cómo funcionan las palabras clave async y await en JavaScript.

1. Función básica con async/await

Se define una función async que retorna un mensaje. Utilizando then, se maneja la promesa y se imprime el mensaje cuando se resuelve.

javascript
async function obtenerMensaje() {
  return "Hola, Mundo Asíncrono";
}

obtenerMensaje().then(console.log); 
// Salida: Hola, Mundo Asíncrono

2. Simulación de una llamada a una API

La función obtenerDatos realiza una llamada fetch a una API, espera la respuesta con await, y luego procesa los datos en formato JSON antes de imprimirlos en consola.

javascript
async function obtenerDatos() {
  const respuesta = await fetch("https://jsonplaceholder.typicode.com/posts/1");
  const datos = await respuesta.json();
  console.log(datos);
}

obtenerDatos();
// Salida: Objeto con los datos del post

3. Manejo de errores con try...catch

Aquí se muestra cómo manejar errores en una función async. Si la respuesta de la API no es válida, se lanza un error personalizado que es capturado y mostrado en consola.

javascript
async function obtenerDatosConError() {
  try {
      const respuesta = await fetch("https://jsonplaceholder.typicode.com/posts/abc");
      if (!respuesta.ok) throw new Error("Error en la respuesta");
      const datos = await respuesta.json();
      console.log(datos);
  } catch (error) {
      console.error("Error capturado:", error.message);
  }
}

obtenerDatosConError();
// Salida: Error capturado: Error en la respuesta

4. Función que espera múltiples Promesas

La función procesarPromesas maneja dos promesas que se resuelven en diferentes tiempos. Usa await para esperar la resolución de cada una antes de mostrar sus resultados en consola.

javascript
async function procesarPromesas() {
  const promesa1 = new Promise(resolve => setTimeout(() => resolve("Promesa 1 resuelta"), 1000));
  const promesa2 = new Promise(resolve => setTimeout(() => resolve("Promesa 2 resuelta"), 2000));

  const resultado1 = await promesa1;
  const resultado2 = await promesa2;

  console.log(resultado1); // Salida: Promesa 1 resuelta
  console.log(resultado2); // Salida: Promesa 2 resuelta
}

procesarPromesas();

5. Usando Promise.all con async/await

Se utilizan varias promesas para realizar múltiples solicitudes a la API de manera concurrente. Promise.all espera que todas las promesas se resuelvan antes de imprimir los resultados en consola.

javascript
async function obtenerTodosLosDatos() {
  const urls = [
      "https://jsonplaceholder.typicode.com/posts/1",
      "https://jsonplaceholder.typicode.com/posts/2",
      "https://jsonplaceholder.typicode.com/posts/3"
  ];

  const promesas = urls.map(url => fetch(url).then(res => res.json()));
  const resultados = await Promise.all(promesas);

  console.log(resultados);
  // Salida: Array con los datos de los tres posts
}

obtenerTodosLosDatos();

6. Función que retorna valores asíncronos

La función sumaAsincrona retorna una suma de dos números. Aunque la operación es sencilla, se maneja de manera asíncrona utilizando await para obtener y mostrar el resultado en consola.

javascript
async function sumaAsincrona(a, b) {
  return a + b;
}

async function ejecutarSuma() {
  const resultado = await sumaAsincrona(5, 10);
  console.log("Resultado:", resultado); // Salida: Resultado: 15
}

ejecutarSuma();
  Ventajas de async/await

  1. Código más legible y fácil de entender.
  2. Simplifica el manejo de Promesas.
  3. Permite escribir flujos asíncronos de forma más secuencial.

  Desventajas de async/await

  1. Puede ser incompatible con navegadores más antiguos sin transpilers.
  2. El uso incorrecto puede causar bloqueos innecesarios si no se manejan adecuadamente las Promesas.