El problema: operaciones lentas
Casi todo lo interesante en un backend es asíncrono: leer un archivo, hablar con una base de datos, llamar a otra API. Node ha tenido tres estilos para manejarlo, y conviene conocer los tres.
1. Callbacks y el patrón (err, data)
El estilo original de Node: pasas una función de continuación (callback)
que Node llama cuando termina. Por convención, el primer argumento es el
error (o null si todo fue bien) y el segundo, los datos:
const fs = require("fs");
fs.readFile("datos.txt", "utf8", (err, data) => {
if (err) {
console.error("Falló:", err.message);
return; // ¡siempre comprobar err primero!
}
console.log(data);
});
El problema: anidar varios callbacks lleva al "callback hell", código en forma de pirámide difícil de leer y de manejar errores.
2. Promesas
Una promesa representa un valor que estará disponible en el futuro. Se
encadena con .then() (éxito) y .catch() (error), de forma plana:
const fs = require("fs/promises"); // versión basada en promesas de fs
fs.readFile("datos.txt", "utf8")
.then((data) => console.log(data))
.catch((err) => console.error(err.message));
3. async / await
Es azúcar sintáctico sobre las promesas: escribes código asíncrono que se
lee como síncrono. Una función async siempre devuelve una promesa, y await
pausa hasta que la promesa se resuelve. Los errores se capturan con
try / catch normal:
import { readFile } from "fs/promises";
async function leer(ruta) {
try {
const data = await readFile(ruta, "utf8");
return data;
} catch (err) {
console.error("No se pudo leer:", err.message);
throw err; // o devolver un valor por defecto
}
}
En el ejercicio de este módulo escribirás funciones
asyncque reciben una fuente de datos y la esperan conawait. Así practicas el flujo asíncrono sin depender de archivos reales (que no existen en este entorno).
Ejemplos
Una función async devuelve una promesa; se espera con await
async function doble(n) {
return n * 2; // aunque no haya await, devuelve una promesa
}
const r = await doble(21);
console.log(r); // 42