XMLHttpRequest
Aprende a usar XMLHttpRequest (XHR) en JavaScript: métodos open y send, readyState, eventos load/error/timeout, parseo de JSON, POST y abortar peticiones.
JavaScript es un lenguaje de programación esencial para el desarrollo web que permite crear experiencias de usuario dinámicas e interactivas. Una de las características clave de JavaScript es su capacidad para comunicarse con servidores, recuperar datos y actualizar páginas web de forma asíncrona. Esto se logra principalmente mediante el uso de XMLHttpRequest (XHR). Este artículo profundiza en XMLHttpRequest, abarcando sus métodos, propiedades y aplicaciones prácticas, con múltiples ejemplos de código para facilitar el aprendizaje.
XMLHttpRequest funciona con funciones de devolución de llamada (callbacks). Para código nuevo, generalmente preferirás la Fetch API, que está basada en promesas y funciona de forma limpia con async/await. XHR sigue siendo valioso de entender: lo encontrarás en bases de código antiguas y es el único API nativo que reporta progreso de subida con granularidad fina.
Esta página cubre qué es un objeto XHR, cómo configurar y enviar una petición con open y send, cómo leer la respuesta mediante readyState y los eventos load/error/timeout, cómo parsear JSON, cómo enviar datos con POST, cómo abortar una petición y cómo se compara XHR con fetch.
Comprendiendo XMLHttpRequest
XMLHttpRequest (XHR) es un objeto nativo del navegador que permite a JavaScript enviar peticiones HTTP o HTTPS a un servidor y recibir la respuesta sin recargar la página. El "XML" del nombre es histórico; XHR puede transferir cualquier formato de texto o binario, y JSON es con diferencia el más común hoy en día. Esta capacidad de comunicarse con un servidor en segundo plano es la base de lo que se conocía como AJAX (Asynchronous JavaScript and XML).
El ciclo de vida de una petición es siempre el mismo: crear el objeto, abrirlo (configurar el método y la URL), adjuntar manejadores de eventos para reaccionar al resultado y luego enviarlo.
Creando un objeto XMLHttpRequest
Primero, crea una instancia:
const xhr = new XMLHttpRequest();Una sola instancia de XMLHttpRequest gestiona una petición. Para hacer una segunda petición, crea un nuevo objeto.
Realizando una petición HTTP
Una vez que el objeto existe, configúralo con open y luego despáchalo con send.
El método open
open inicializa una petición pero no la envía aún. Acepta varios parámetros:
xhr.open(method, url, async, user, password);method: El método HTTP a usar, por ejemplo'GET'o'POST'.url: La URL a la que se envía la petición.async: Un boolean que indica si la petición es asíncrona. Su valor predeterminado estrue, y casi siempre debes dejarlo así (ver la advertencia a continuación).user: Nombre de usuario opcional para autenticación HTTP.password: Contraseña opcional para autenticación HTTP.
Ejemplo:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);Las peticiones síncronas (xhr.open(method, url, false)) bloquean la página hasta que llega la respuesta y están obsoletas en el hilo principal. Mantén siempre async en true.
El método send
send despacha la petición al servidor. Los manejadores de eventos deben adjuntarse antes de llamarlo. Para una petición GET, llámalo sin argumentos. Para un POST, pasa el cuerpo de la petición como argumento.
Ejemplo de una petición GET:
xhr.send();Ejemplo de una petición POST con datos codificados en formulario:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('param1=value1¶m2=value2');El método setRequestHeader añade una cabecera HTTP a la petición saliente y debe llamarse después de open pero antes de send.
Gestionando las respuestas del servidor
Para gestionar las respuestas del servidor se pueden usar varios escuchadores de eventos.
El evento onreadystatechange
El evento onreadystatechange se dispara cada vez que cambia la propiedad readyState. La propiedad readyState indica el estado del XMLHttpRequest.
0: UNSENT1: OPENED2: HEADERS_RECEIVED3: LOADING4: DONE
Una petición está terminada y fue exitosa solo cuando readyState es 4 (DONE) y el status HTTP está en el rango de éxito (típicamente 200). Verificar únicamente readyState === 4 es un error común, ya que el servidor puede haber respondido con 404 o 500.
Ejemplo:
Aunque onreadystatechange funciona, el código moderno generalmente prefiere onload y onerror para un manejo de peticiones más simple y legible. onreadystatechange se usa principalmente cuando necesitas rastrear estados intermedios (como el progreso o la recepción de cabeceras).
El evento load
El evento load se dispara una vez que la respuesta ha llegado por completo. Es más simple que onreadystatechange porque no tienes que comprobar readyState tú mismo; solo se dispara en la etapa DONE. Aun así debes verificar status para distinguir un éxito real de un error HTTP.
Ejemplo:
El evento progress
Para descargas grandes puedes reportar el progreso con el evento progress. Cuando el servidor envía una cabecera Content-Length, el evento es determinado (lengthComputable es true) y puedes calcular un porcentaje:
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`Downloaded ${percent}%`);
}
};Para rastrear una subida en su lugar, adjunta manejadores a xhr.upload (xhr.upload.onprogress). El objeto de progreso de subida es la característica que Fetch todavía no puede replicar completamente.
Gestionando errores
El código robusto debe manejar los fallos. Dos eventos cubren los casos de error:
onerrorse dispara ante un fallo a nivel de red: la petición nunca llegó al servidor, falló el DNS, CORS la bloqueó, etc. Ten en cuenta que un HTTP404o500no es un error de red: disparaload, noerror, por lo que aun así debes inspeccionarstatus.ontimeoutse dispara si la petición tarda más dexhr.timeoutmilisegundos. Un timeout de0(el valor predeterminado) significa sin límite.
Ejemplo:
Parseando respuestas JSON
Las respuestas del servidor son en su mayoría JSON. El enfoque más sencillo es leer el texto sin procesar de xhr.responseText y parsearlo tú mismo con JSON.parse:
De manera alternativa, establece xhr.responseType = 'json' antes de enviar y el navegador parsea el cuerpo por ti. El valor parseado estará entonces disponible en xhr.response (no en xhr.responseText):
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.responseType = 'json';
xhr.onload = function() {
if (xhr.status === 200) {
console.log('title: ' + xhr.response.title); // already an object
}
};
xhr.send();responseType también acepta 'text', 'blob', 'arraybuffer' y 'document' para cargas que no son JSON.
Enviando datos con POST
Para enviar un cuerpo JSON, establece la cabecera Content-Type y serializa tu object con JSON.stringify:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 201) { // 201 Created
console.log('Created:', xhr.responseText);
}
};
xhr.send(JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }));Para envíos de formularios tradicionales, envía un object FormData en su lugar; el navegador establece el Content-Type multipart correcto automáticamente, así que no llames a setRequestHeader para ello.
Abortando una petición
Llama a xhr.abort() para cancelar una petición en curso, por ejemplo cuando el usuario navega a otra página o escribe una nueva consulta de búsqueda. Tras abortar, el evento abort se dispara en lugar de load:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.onabort = () => console.log('Request was cancelled');
xhr.send();
// Later, cancel it:
xhr.abort();El equivalente en Fetch utiliza un AbortController.
XMLHttpRequest vs Fetch
| XMLHttpRequest | Fetch | |
|---|---|---|
| Modelo de programación | Callbacks / eventos | Promesas, funciona con await |
| Progreso de subida | Sí (xhr.upload) | No |
| Progreso de descarga | Sí (evento progress) | Mediante streams (más código) |
| Abortar | xhr.abort() | AbortController |
| Rechaza en error HTTP | No, compruebas status | No, compruebas response.ok |
Para la mayoría del código nuevo, prefiere Fetch. Usa XHR cuando necesites reportar el progreso de subida con granularidad fina o debas dar soporte a entornos muy antiguos.
Conclusión
XMLHttpRequest permite a JavaScript intercambiar datos con un servidor en segundo plano: creas el objeto, lo abres con open, adjuntas manejadores load/error/timeout y lo envías con send. Recuerda verificar tanto readyState como status, parsear JSON tú mismo o mediante responseType, y usar abort() para cancelar peticiones obsoletas. Para la mayoría del código nuevo, la Fetch API basada en promesas es la mejor opción predeterminada, pero entender XHR te mantiene al día en bases de código antiguas y en los casos, como el progreso de subida, donde todavía gana.