1.1 Conceptos básicos de asincronía
La asincronía en JavaScript permite realizar tareas en segundo plano sin bloquear el flujo principal de ejecución. Esto es especialmente importante para tareas que pueden llevar mucho tiempo, como solicitudes de red, lectura de archivos y temporizadores. Ahora veremos los conceptos básicos de la programación asincrónica en JavaScript y daremos ejemplos de uso.
Carácter de un solo hilo de JavaScript
JavaScript es un lenguaje monohilo: esto significa que ejecuta el código secuencialmente en un solo hilo. Sin embargo, las operaciones asincrónicas permiten realizar tareas en segundo plano, liberando el hilo principal para otras tareas.
Event Loop (Bucle de eventos)
El bucle de eventos (Event Loop) es un mecanismo clave que permite a JavaScript manejar tareas asincrónicas. El bucle de eventos gestiona la cola de mensajes (Message Queue) y la cola de microtareas (Microtask Queue), asegurando la ejecución de operaciones asincrónicas.
- Cola de mensajes: contiene tareas como los manejadores de eventos, solicitudes de red y temporizadores. Las tareas en esta cola se ejecutan secuencialmente.
- Cola de microtareas: contiene tareas con mayor prioridad en comparación con las tareas de la cola de mensajes. Ejemplos incluyen completar promesas y llamadas a funciones de retorno (callbacks) en microtareas.
El bucle de eventos constantemente revisa ambas colas y ejecuta tareas de ellas cuando el hilo principal se libera.
Operaciones asincrónicas
Las operaciones asincrónicas permiten realizar tareas en segundo plano. Los principales ejemplos de operaciones asincrónicas incluyen:
- Temporizadores (
setTimeout
,setInterval
) - Manejadores de eventos
- Solicitudes de red (por ejemplo,
XMLHttpRequest
,Fetch API
) - Lectura/escritura de archivos (en Node.js)
Veamos algunos ejemplos de operaciones asincrónicas.
1.2 Temporizadores
Los temporizadores permiten realizar tareas con retraso o en intervalos regulares de tiempo.
Ejemplo de uso de setTimeout
En este ejemplo, setTimeout
establece la ejecución de una función después de 2 segundos. Como resultado, primero se muestra
Start
y End
, y luego, después de 2 segundos, se muestra Executed after 2 seconds
.
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
console.log('End');
Ejemplo de uso de setInterval
En este ejemplo, setInterval
ejecuta una función cada segundo, incrementando un contador y mostrando su valor.
Cuando el contador llega a 5, el intervalo se limpia usando clearInterval
:
let counter = 0;
const intervalID = setInterval(() => {
counter++;
console.log(`Counter: ${counter}`);
if (counter >= 5) {
clearInterval(intervalID);
}
}, 1000);
1.3 Manejadores de eventos
Los manejadores de eventos permiten ejecutar código en respuesta a acciones del usuario u otros eventos.
Ejemplo de uso de manejadores de eventos
En este ejemplo, se agrega un manejador de eventos click
a un botón. Cuando el usuario hace clic en el botón,
se muestra el mensaje Button clicked!
:
<!DOCTYPE html>
<html>
<head>
<title>Event Handler Example</title>
</head>
<body>
<button id="myButton">Click me</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
});
</script>
</body>
</html>
1.4 Solicitudes de red
Las solicitudes de red permiten realizar solicitudes HTTP asincrónicas al servidor.
Ejemplo de uso de XMLHttpRequest
En este ejemplo, se crea una solicitud GET asincrónica a una API, y cuando la solicitud se completa, la respuesta se muestra en la consola:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
}
};
xhr.send();
1.5 Event Loop en acción
Para entender mejor cómo funciona el Event Loop, veamos el siguiente ejemplo:
console.log('Start');
setTimeout(() => {
console.log('Timeout 1');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
});
setTimeout(() => {
console.log('Timeout 2');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 2');
});
console.log('End');
La salida esperada será la siguiente:
Start
End
Promise 1
Promise 2
Timeout 1
Timeout 2
En este ejemplo, primero se ejecutan las operaciones síncronas (console.log('Start')
y console.log('End')
). Luego
se ejecutan las microtareas (manejadores de promesas) y solo después de eso se ejecutan las tareas de la cola de mensajes (setTimeout
).
GO TO FULL VERSION