1.2Asincronía: callbacks, promesas, async/await
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.
La pirámide infernal (no haga nunca esto)
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
- Escriba una operación asíncrona con un callback (p. ej. fs.readFile) y detecte la convención error-first (err, data).
- Encadene dos operaciones dependientes con callbacks anidados: compruebe el inicio del callback hell.
- Reescriba con promesas y el encadenamiento .then().then().catch(): observe la mejor legibilidad.
- 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.
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.
- 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.
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.
¿Qué hace await en una función async?