1.2 · Asincronía: callbacks, promesas, async/await

Nivel 1 · JavaScript y bases de Node

1.2Asincronía: callbacks, promesas, async/await

Objetivo: entender y manejar la asincronía en sus tres formas (callbacks, promesas, async/await), y priorizar async/await — el hito central de Node.
Tiempo estimado: 13 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

Lo esencial en 30 segundos

  • La asincronía = «cuando esté listo»: las operaciones lentas se lanzan y su resultado llega más tarde — no se puede leer en la línea siguiente.
  • Callbacks → callback hell: la convención error-first (error como primer argumento) funciona, pero los callbacks dependientes se anidan en una pirámide ilegible.
  • async/await a priorizar: await espera sin bloquear, el código se lee como sincronía, errores vía try/catch — construido sobre las promesas.

Minicaso hilado — Salim espera sus archivos en Argel

Salim debe leer un archivo de configuración, luego consultar una base con su contenido y después llamar a una API: escribe tres callbacks anidados y se pierde en la indentación — peor aún, su console.log muestra undefined porque lee el resultado antes de que llegue. Reescribe todo en async/await con try/catch: tres líneas await legibles en orden, y el error gestionado en un solo lugar. El error del undefined desaparece con el antiguo modelo mental.

Continuación del caso 1.1 — Salim pasa del lenguaje al corazón de Node: la asincronía.

1. De los callbacks al callback hell

La asincronía es EL concepto central de Node: las operaciones lentas (leer un archivo, consultar una base, llamar a una API) se lanzan y su resultado llega más tarde. El mecanismo histórico es el callback: una función pasada como argumento, llamada al final de la operación — con la convención error-first (error como primer argumento, null si todo va bien). Encadenar operaciones dependientes lleva al callback hell: una pirámide ilegible, difícil de gestionar sobre todo para los errores.

Salim escribe fs.readFile('config.txt', (err, data) => ...) y detecta la convención error-first: probar err primero, explotar data después — nunca al revés.

2. Las promesas: el valor futuro encadenado

Una promesa representa un valor futuro — el resultado de una operación aún no terminada. Tres estados: pending (en espera), fulfilled (éxito, un valor), rejected (error). Se reacciona con .then() (éxito) y .catch() (error), y las promesas se encadenan en lugar de anidarse — mucho más legible que el callback hell.

Salim reescribe su lectura de archivo en fs.promises.readFile(...).then().catch(): la cadena sustituye la pirámide y el error aterriza en un solo .catch().

3. async/await: la legibilidad (a priorizar)

La verdadera revolución de legibilidad es async/await: en una función async, await espera el resultado sin bloquear (azúcar sintáctico sobre las promesas) y el código asíncrono se lee como sincronía, línea tras línea, con un try/catch clásico para los errores. Es el estilo dominante y recomendado: encontrará las tres formas en el código existente, pero escribirá principalmente en async/await.

Salim termina con const data = await fs.promises.readFile(...) en un try/catch: tres operaciones en tres líneas ordenadas — su código asíncrono por fin se lee como una receta.

Error frecuente de interpretación: «await bloquea el programa como un sleep» o «se puede leer el resultado en la línea siguiente a la llamada». No. await solo suspende la función async mientras espera la promesa, sin bloquear el bucle de eventos — las demás peticiones siguen atendiéndose. Y el resultado de una operación asíncrona aún no existe en la línea siguiente: hay que consumirlo en el callback, el .then() o después de un await.
La evolución de la asincronía Hacia la legibilidad Callbacks error-first,riesgo de callback hellPromesas Valor futuro,encadenamiento .then/.catchasync/await Se lee comosincronía, try/catch
Hacia la legibilidad
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Lanzar, no esperar: Salim lanza la lectura del archivo y da un callback «cuando esté listo» (error-first) en lugar de leer el resultado en la línea siguiente. 2) Encadenar: sustituye los callbacks anidados por promesas en .then().catch() — se acabó la pirámide. 3) Aplanar: pasa a async/await en un try/catch, tres operaciones en tres líneas legibles. Lección: distinguir «lanzar» y «obtener el resultado» es el modelo mental que desbloquea todo Node.

La pirámide infernal (no haga nunca esto)

«Encadeno seis callbacks anidados, aún me oriento… en el tercer nivel.»
Veredicto: en el cuarto nivel, ni el autor se orienta — y la gestión de errores queda esparcida por todas partes.
«Mi console.log muestra undefined, el archivo debe de estar vacío.»
Veredicto: el archivo está lleno, es su lectura la que llega demasiado pronto — el resultado aún no existe en la línea siguiente.

Moraleja: anidar es enterrarse — aplanar con await es respirar.

🔎 Ejemplo concreto — 🔎 Ejemplo concreto — El mismo código en async/await

Legible, no bloqueante, errores gestionados — el estilo que hay que escribir.

const fs = require("fs");
async function lire() {
  try {
    const data = await fs.promises.readFile("f.txt", "utf8");
    console.log(data);
  } catch (err) {
    console.error("Echec :", err.message);
  }
}
lire();

Lectura: await espera el contenido sin bloquear; en caso de fallo, el catch muestra el mensaje.

Lección: Promise.all para lanzar varias operaciones independientes en paralelo.

Vocabulario de la sección

Callback
Función pasada como argumento, llamada cuando termina una operación asíncrona. Convención Node error-first: primer argumento = error (null si OK), luego el resultado.

Ejemplo sencillo: El (err, data) => ... de la lectura de archivo de Salim.

Lo que no es: una función llamada inmediatamente con un resultado ya listo.

Reflejo: probar err primero, explotar el resultado después.

Callback hell
Anidación ilegible de callbacks al encadenar operaciones asíncronas dependientes; problema resuelto por las promesas y async/await.

Ejemplo sencillo: La pirámide de tres niveles del primer script de Salim.

Lo que no es: tres await alineados legiblemente en una función async.

Reflejo: en cuanto se anida, pasar a las promesas.

Promesa
Objeto que representa un valor futuro. Tres estados: pending, fulfilled (éxito), rejected (error). Reacciones vía .then() y .catch().

Ejemplo sencillo: La promesa devuelta por fs.promises.readFile en casa de Salim.

Lo que no es: el contenido del archivo disponible en la línea siguiente.

Reflejo: .then para el éxito, .catch para el error.

async/await
Sintaxis moderna construida sobre las promesas: await espera el resultado sin bloquear; el código asíncrono se lee como sincronía. Errores vía try/catch.

Ejemplo sencillo: Los tres await ordenados del script final de Salim.

Lo que no es: un sleep que bloquearía toda la aplicación.

Reflejo: escribir en async/await por defecto.

error-first / try-catch
Gestión de errores según la forma: error como primer argumento de los callbacks, .catch() para las promesas, try/catch alrededor de los await.

Ejemplo sencillo: El try/catch único que protege las tres operaciones de Salim.

Lo que no es: ignorar el error y dejar que la aplicación falle en silencio.

Reflejo: siempre un camino de error explícito.

En la práctica — En la práctica — Dominar la asincronía

  1. Escriba una operación asíncrona con un callback (p. ej. fs.readFile) y detecte la convención error-first (err, data).
  2. Encadene dos operaciones dependientes con callbacks anidados: compruebe el inicio del callback hell.
  3. Reescriba con promesas y el encadenamiento .then().then().catch(): observe la mejor legibilidad.
  4. Reescriba por fin en async/await (función async, await delante de las operaciones, try/catch para los errores): el código se lee como sincronía.
Entiende y maneja la asincronía en sus tres formas (callbacks, promesas, async/await), y prioriza async/await para un código legible — el hito central de Node.

Guion listo para decir — explicar la asincronía a un compañero (1 min)

«En Node, una operación lenta se lanza pero su resultado llega más tarde: nunca se lee en la línea siguiente. Se dice +cuando esté listo, haz esto+ — con un callback, un .then() o un await. Por eso escribimos en async/await: el código espera sin bloquear y se lee en orden.»

Para decir ante el error clásico del console.log que muestra undefined.

Modelo copiar-pegar — lectura de archivo async/await

const fs = require("fs");
async function lire(nom) {
  try {
    const data = await fs.promises.readFile(nom, "utf8");
    console.log(data);
    return data;
  } catch (err) {
    console.error("Echec :", err.message);
    return null;
  }
}
lire("f.txt");

Operaciones dependientes

Leer un archivo y luego consultar con su contenido: encadenar los await en orden en un solo try/catch — como Salim con config → base → API.

Operaciones independientes

Varias lecturas sin vínculo entre ellas: lanzarlas juntas con Promise.all para ir rápido en lugar de esperar cada una su turno.

Código antiguo en callbacks

Ante una API error-first existente: entenderla y usarla tal cual para leer, pero escribir todo el código nuevo en async/await.

Error silencioso en Argel

Salim añade sistemáticamente un catch que muestra err.message: un error asíncrono no gestionado hace fallar o miente en silencio — nunca un await sin red.

✓ Buenas prácticas

  • Distinguir «lanzar» y «obtener».
  • Escribir en async/await por defecto.
  • Siempre un camino de error explícito.

✗ Errores frecuentes

  • Leer el resultado en la línea siguiente.
  • Anidar los callbacks dependientes.
  • await sin try/catch.

Puntos clave

  • La ASINCRONÍA es EL concepto central de Node: las operaciones lentas se lanzan y su resultado llega MÁS TARDE.
  • Progresión: CALLBACKS (error-first) → riesgo de callback hell → PROMESAS (encadenamiento .then/.catch) → ASYNC/AWAIT.
  • Una PROMESA = valor futuro (pending/fulfilled/rejected); se reacciona con .then() (éxito) y .catch() (error).
  • ASYNC/AWAIT (a priorizar): await espera sin bloquear, el código se LEE como sincronía, errores vía try/catch.

Reto 48h — aplíquelo mañana

Tome el modelo anterior: lea un archivo real con un callback, reescriba en promesas y luego en async/await con try/catch. Compare las tres versiones. 25 minutos bastan.

Lista de salida de la sección
  • Puedo explicar «lanzar» frente a «obtener el resultado».
  • Sé cuándo usar callback, promesa o await.
  • He escrito la misma operación en las tres formas.
Autoevaluación 1-5: ¿detecto el error del resultado leído demasiado pronto?¿mi código nuevo está en async/await con try/catch?

Preguntas frecuentes

¿Por qué la asincronía desconcierta tanto al principio y cómo entenderla de verdad?

Rompe con la intuición «cada línea termina antes de la siguiente». La trampa clásica: leer el resultado en la línea siguiente a la llamada — se obtiene undefined porque la operación aún no ha terminado. El modelo mental que adoptar: distinguir «lanzar» (inmediato) y «obtener el resultado» (más tarde, vía callback, .then() o await). Piense «cuando esté listo, haz esto» — como un pedido en el restaurante: no se queda plantado en la cocina, sigue y le sirven cuando está listo. Una vez superado ese clic, como para Salim, todo Node se vuelve legible.

Callbacks, promesas o async/await: ¿cuál usar hoy?

Debe conocer las tres pero escribir en async/await. Callbacks: la forma histórica, para entender el código existente y las API clásicas (convención error-first) — evite anidarlos. Promesas: la base moderna (async/await está construido sobre ellas) — para entender, y útiles directamente para el paralelismo (Promise.all). async/await: su estilo por defecto para todo código nuevo — legible, errores vía try/catch. Regla práctica: escribir en await, entender las promesas, saber leer los callbacks.

Comprueba tu comprensión

¿Qué hace await en una función async?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 1Preguntas inéditas y más difíciles para validar tus conocimientos