7 ‐ ¿Qué es una promesa en JS? - Jabes-Gonzalez/Checkpoint-8 GitHub Wiki
Una promesa es un objeto especial que actúa como contenedor para un valor que puede no estar disponible aún, pero que se resolverá en el futuro. Permite manejar operaciones asíncronas de manera más elegante que los callbacks tradicionales.
Estructura básica:
const miPromesa = new Promise((resolve, reject) => {
// Lógica asíncrona
if (operacionExitosa) {
resolve(valor); // Éxito
} else {
reject(error); // Fracaso
}
});
Estados de una Promesa
- Pending (pendiente): Estado inicial, la operación está en proceso.
- Fulfilled (cumplida): La operación terminó exitosamente y la promesa tiene un valor.
- Rejected (rechazada): La operación falló y la promesa tiene una razón de rechazo.
Creación de Promesas
Constructor básico
const promesa = new Promise((resolve, reject) => {
setTimeout(() => {
const exito = Math.random() > 0.5;
exito ? resolve("¡Éxito!") : reject("Error");
}, 1000);
});
Ejemplo práctico: Carga de scripts
function cargarScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve(src);
script.onerror = () => reject(new Error(`Error al cargar ${src}`));
document.head.append(script);
});
}
Consumo de Promesas
Métodos principales
Ejemplo:
cargarScript('mi-script.js')
.then(src => console.log(`${src} cargado`))
.catch(err => console.error(err))
.finally(() => console.log('Proceso finalizado'));
Encadenamiento de Promesas
Las promesas pueden encadenarse para ejecutar operaciones secuenciales:
cargarScript('script1.js')
.then(() => cargarScript('script2.js'))
.then(() => cargarScript('script3.js'))
.catch(err => console.error(err));
Manejo de errores en cadena:
cargarDatos()
.then(procesar)
.then(mostrar)
.catch(err => {
console.error('Error en la cadena:', err);
return valorPorDefecto;
});
Métodos Estáticos de Promise
Ejemplo con Promise.all():
const promesas = [
fetch('/api/datos1'),
fetch('/api/datos2'),
fetch('/api/datos3')
];
Promise.all(promesas)
.then(respuestas => {
// Todas las respuestas exitosas
})
.catch(err => {
// Manejo de primer error
});
Ventajas sobre Callbacks Tradicionales
Ejemplos Avanzados
Convertir callback a promesa
function leerArchivo(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
Uso con async/await (ES2017)
async function procesoAsync() {
try {
const dato1 = await obtenerDato1();
const dato2 = await obtenerDato2(dato1);
return procesar(dato2);
} catch (error) {
manejarError(error);
}
}
Errores Comunes y Buenas Prácticas
Errores frecuentes:
- Olvidar el return en .then()
- No manejar errores con .catch()
- Crear promesas innecesarias ("Promise constructor antipattern")
Buenas prácticas:
- Siempre retornar promesas en cadenas
- Usar Promise.all() para operaciones paralelas
- Preferir async/await para código más legible
- Siempre manejar errores con .catch() o try/catch en async/await.
- Evitar crear promesas innecesarias (no envolver promesas existentes).
Las promesas revolucionaron el manejo de operaciones asíncronas en JavaScript, ofreciendo:
- Mejor legibilidad con encadenamiento claro
- Manejo centralizado de errores
- Compatibilidad con operaciones paralelas y secuenciales
- Base para características modernas como async/await Al dominar las promesas, los desarrolladores pueden escribir código asíncrono más mantenible y robusto, preparando el terreno para técnicas avanzadas de programación asíncrona.
Encadenamiento y Flujo de Promesas
Las promesas permiten encadenar operaciones asíncronas secuenciales usando .then()
. Cada .then()
devuelve una nueva promesa, lo que permite construir flujos complejos y controlados.
const miPromesa = new Promise((resolve, reject) => {
setTimeout(() => resolve("Primer paso"), 500);
});
miPromesa
.then(resultado => {
console.log(resultado); // "Primer paso"
return "Segundo paso";
})
.then(segundo => {
console.log(segundo); // "Segundo paso"
return "Tercer paso";
})
.then(console.log) // "Tercer paso"
.catch(console.error)
.finally(() => console.log("Cadena completada"));
Cada .then()
recibe el resultado del anterior. Si ocurre un error, la cadena salta al .catch()
final.
Manejo de Errores y Garantías de las Promesas
- Las promesas capturan errores tanto de operaciones asíncronas como de excepciones lanzadas en los callbacks.
- Los errores pueden manejarse centralizadamente con
.catch()
, lo que evita la "pirámide de la perdición" de los callbacks anidados. - Es posible continuar la cadena después de un
.catch()
:
new Promise((resolve, reject) => {
resolve();
})
.then(() => {
throw new Error("Algo falló");
})
.catch(() => {
console.log("Manejo del error");
})
.then(() => {
console.log("Continúa la cadena");
});
Esto imprime: Manejo del error / Continúa la cadena.
Métodos Estáticos Avanzados
Ejemplo con Promise.all():
Promise.all([
fetch('/api/uno'),
fetch('/api/dos')
])
.then(([res1, res2]) => Promise.all([res1.json(), res2.json()]))
.then(([json1, json2]) => {
console.log(json1, json2);
})
.catch(console.error);
Ambas peticiones deben resolverse para continuar.
Patrones Avanzados y Consejos Prácticos
- Encadenar Promesas Dinámicamente: Puedes construir cadenas de promesas en bucles o con arrays usando
.reduce()
para operaciones secuenciales. - Evitar el “Promise constructor antipattern”: No envuelvas promesas dentro de nuevas promesas innecesariamente.
- Múltiples
.then()
sobre la misma promesa: Cada uno se ejecuta en orden de registro, todos reciben el mismo valor resuelto. - Transformar funciones de callback a promesa: Envolver APIs antiguas con el constructor
Promise
para modernizar el código.
Ejemplo Avanzado: Promesas Encadenadas y Manejo de Errores
function obtenerNumero() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const num = Math.floor(Math.random() * 10);
num > 5 ? resolve(num) : reject('Número muy bajo');
}, 500);
});
}
obtenerNumero()
.then(num => {
console.log('Número:', num);
return num * 2;
})
.then(doble => {
console.log('Doble:', doble);
return Promise.reject('Error simulado');
})
.catch(error => {
console.error('Error capturado:', error);
return 0;
})
.then(final => {
console.log('Valor final:', final); // 0
});
Demuestra flujo, manejo de errores y continuación tras un catch.
Recursos y Consejos de Expertos
- Aprovecha los métodos estáticos (
all
,allSettled
,race
,any
) para controlar flujos complejos. - Usa
.finally()
para limpiar recursos o actualizar la UI, sin importar el resultado. - Encadena siempre los retornos en
.then()
para evitar perder el flujo de datos y errores. - Consulta guías avanzadas para patrones profesionales y casos de uso reales.