W3docs

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.

Información

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 es true, 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);
Advertencia

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&param2=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: UNSENT
  • 1: OPENED
  • 2: HEADERS_RECEIVED
  • 3: LOADING
  • 4: 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:

javascript— editable
Nota

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:

javascript— editable

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:

  • onerror se 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 HTTP 404 o 500 no es un error de red: dispara load, no error, por lo que aun así debes inspeccionar status.
  • ontimeout se dispara si la petición tarda más de xhr.timeout milisegundos. Un timeout de 0 (el valor predeterminado) significa sin límite.

Ejemplo:

javascript— editable

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:

javascript— editable

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

XMLHttpRequestFetch
Modelo de programaciónCallbacks / eventosPromesas, funciona con await
Progreso de subidaSí (xhr.upload)No
Progreso de descargaSí (evento progress)Mediante streams (más código)
Abortarxhr.abort()AbortController
Rechaza en error HTTPNo, compruebas statusNo, 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.

Práctica

Práctica
¿Cuáles de las siguientes afirmaciones sobre XMLHttpRequest son correctas?
¿Cuáles de las siguientes afirmaciones sobre XMLHttpRequest son correctas?
Was this page helpful?