Programación asíncrona en JavaScript: guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a la programación asíncrona en JavaScript



JavaScript es un lenguaje de un solo hilo con un bucle de eventos (Event Loop) no bloqueante. Esto significa que en un momento dado solo se ejecuta una operación, pero gracias a la asincronía podemos no esperar la finalización de tareas lentas (lectura de archivos, solicitudes a API, temporizadores) y continuar ejecutando otro código. En este artículo analizaremos todos los conceptos clave de la programación asíncrona en el entorno Node.js, desde callbacks hasta la sintaxis moderna async/await.



La programación asíncrona permite crear aplicaciones de servidor de alto rendimiento que procesan miles de solicitudes sin bloquear el hilo principal. Comprender este paradigma es una habilidad obligatoria para cualquier desarrollador de JavaScript.



1. Funciones Callback y el problema del Callback Hell



Históricamente, el primer mecanismo de asincronía en JavaScript fueron los callbacks — funciones que se ejecutan después de completar una operación asíncrona.



Ejemplo de un callback simple



const fs = require('fs');

// Lectura asíncrona de archivofs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('Error de lectura:', err); return; } console.log('Contenido del archivo:', data);});

console.log('Este mensaje aparecerá antes que el contenido del archivo');


El problema surge cuando se necesitan ejecutar varias operaciones asíncronas secuenciales. El anidamiento de callbacks crece y el código se vuelve ilegible — este fenómeno se conoce como "Callback Hell" o "Infierno de callbacks".



Ejemplo de Callback Hell



fs.readFile('file1.txt', (err, data1) => {    if (err) throw err;    fs.readFile('file2.txt', (err, data2) => {        if (err) throw err;        fs.readFile('file3.txt', (err, data3) => {            if (err) throw err;            // Procesamiento de los tres archivos            console.log(data1 + data2 + data3);        });    });});


Este código es difícil de mantener, probar y depurar. La solución a este problema llegó con la aparición de las Promesas.



2. Promise: liberación del Callback Hell



Una Promise (promesa) es un objeto que representa el resultado de una operación asíncrona: ejecución exitosa (resolve) o error (reject). Las Promesas permiten construir cadenas de llamadas, haciendo el código más plano y legible.



Creación y uso de una Promise



// Función que devuelve una Promiseconst readFilePromise = (filename) => {    return new Promise((resolve, reject) => {        fs.readFile(filename, 'utf8', (err, data) => {            if (err) reject(err);            else resolve(data);        });    });};

// UsoreadFilePromise('file1.txt') .then(data => { console.log('Archivo leído:', data); return readFilePromise('file2.txt'); }) .then(data => { console.log('Segundo archivo:', data); }) .catch(err => { console.error('Error:', err); });


Métodos Promise.all y Promise.race



Para la ejecución paralela de varias operaciones asíncronas existen útiles métodos estáticos:



  • Promise.all() — espera la ejecución de todas las promesas. Si al menos una falla con error, toda la Promise.all pasa al estado rejected.
  • Promise.race() — devuelve el resultado de la primera promesa completada (éxito o error).
  • Promise.allSettled() — espera la finalización de todas las promesas, devuelve los resultados de cada una (éxito o error).


const promise1 = readFilePromise('file1.txt');const promise2 = readFilePromise('file2.txt');const promise3 = readFilePromise('file3.txt');

Promise.all([promise1, promise2, promise3]) .then(results => { console.log('Todos los archivos leídos:', results); }) .catch(err

Blogs

Book Recommendations