JavaScript opera con un modelo de ejecución de un solo hilo, lo que significa que solo puede procesar una instrucción a la vez. Sin embargo, cuando se encuentran operaciones que implican latencia, como consultsa de red o temporizadores, el hilo principal no debe detener su flujo. Para resolver esto, se incorpora la programación asíncrona, que permite que el código continúe su ejecución sin bloquearse esperando la finalización de dichas operaciones.
Contraste entre Enfoques Síncronos y Asíncronos
En un entorno asíncrono, las operaciones que requieren tiempo se delegan al navegador o al entorno de ejecución, liberando el hilo principal. Ejemplo ilustrativo:
console.log('primer mensaje');
setTimeout(() => {
console.log('mensaje diferido');
}, 1500);
console.log('segundo mensaje');
La consola mostrará: 'primer mensaje', 'segundo mensaje', y luego 'mensaje diferido' tras 1.5 segundos, demostrando la no obstrucción.
En cambio, la programación síncrona ejecuta las operaciones en serie, deteniendo el progreso hasta completar cadda tarea. Ejemplo:
console.log('inicio');
while (Date.now() - inicio < 2000) {
// Simulación de operación bloqueante
}
console.log('fin');
Este código bloquea el hilo por 2 segundos antes de imprimir 'fin', ilustrando el comportamiento obstructivo.
Escenarios Prácticos de Uso Asíncrono
La asincronía es vital en contextos como:
- Peticiones HTTP: Al hacer solicitudes a APIs, se evita congelar la interfaz de usuario.
- Manipulación de recursos multimedia: Cargar imágenes o scripts de forma no bloqueante.
console.log('iniciando carga');
const solicitud = new XMLHttpRequest();
solicitud.open('GET', '/api/datos');
solicitud.onload = () => {
if (solicitud.status === 200) {
console.log('datos recibidos:', solicitud.responseText);
}
};
solicitud.send();
console.log('petición enviada');
Para imágenes, se gestiona el evento onload:
console.log('preparando imagen');
const elementoImagen = document.createElement('img');
elementoImagen.addEventListener('load', () => {
console.log('imagen renderizada');
});
elementoImagen.src = '/recurso/foto.jpg';
console.log('asignación de fuente completada');
Los temporizadores también son ejemplos clásicos:
console.log('valor inicial');
const temporizador = setTimeout(() => {
console.log('ejecución diferida');
}, 800);
console.log('valor final');
Gestión de Imágenes con el Patrón Promise
Las Promises facilitan el manejo de flujos asíncronos complejos. Aquí se implementa una función para cargar imágenes encadenando operaciones:
function obtenerImagen(url) {
return new Promise((exito, fallo) => {
const imagen = new Image();
imagen.onload = () => exito(imagen);
imagen.onerror = () => fallo(new Error(`Error cargando ${url}`));
imagen.src = url;
});
}
// Demostración de encadenamiento
const origen1 = 'https://ejemplo.com/imagen1.png';
const origen2 = 'https://ejemplo.com/imagen2.png';
obtenerImagen(origen1)
.then(img => {
console.log(`Ancho: ${img.naturalWidth}`);
return obtenerImagen(origen2);
})
.then(segundaImg => {
console.log(`Altura: ${segundaImg.naturalHeight}`);
})
.catch(error => console.error('Fallo:', error.message));