XMLHttpRequest
Aprenda a usar XMLHttpRequest (XHR) em JavaScript para enviar requisições HTTP assíncronas: métodos open e send, readyState, eventos load/error/timeout, análise de JSON, envio de dados com POST e cancelamento de requisições.
JavaScript é uma linguagem de programação essencial para o desenvolvimento web, possibilitando experiências dinâmicas e interativas para o usuário. Um dos principais recursos do JavaScript é a sua capacidade de se comunicar com servidores, recuperar dados e atualizar páginas web de forma assíncrona. Isso é obtido principalmente por meio do XMLHttpRequest (XHR). Este artigo oferece uma análise aprofundada do XMLHttpRequest, incluindo seus métodos, propriedades e aplicações práticas, com diversos exemplos de código para facilitar o aprendizado.
XMLHttpRequest trabalha com funções de callback. Para novos códigos, você geralmente preferirá a Fetch API, que é baseada em promessas e funciona perfeitamente com async/await. Ainda vale a pena entender o XHR: você o encontrará em bases de código mais antigas, e ele continua sendo a única API nativa que reporta o progresso de upload com granularidade.
Esta página aborda o que é um objeto XHR, como configurar e enviar uma requisição com open e send, como ler a resposta por meio de readyState e dos eventos load/error/timeout, como analisar JSON, como fazer POST de dados, como cancelar uma requisição e como o XHR se compara ao fetch.
Entendendo o XMLHttpRequest
XMLHttpRequest (XHR) é um objeto nativo do navegador que permite ao JavaScript enviar uma requisição HTTP ou HTTPS para um servidor e receber a resposta sem recarregar a página. O "XML" no nome é histórico — o XHR pode transferir qualquer formato de texto ou binário, sendo o JSON de longe o mais comum hoje em dia. Essa capacidade de se comunicar com um servidor em segundo plano é a base do que costumava ser chamado de AJAX (Asynchronous JavaScript and XML).
O ciclo de vida de uma requisição é sempre o mesmo: criar o objeto, abrir (configurar método e URL), anexar manipuladores de eventos para reagir ao resultado e, em seguida, enviar.
Criando um Objeto XMLHttpRequest
Primeiro, crie uma instância:
const xhr = new XMLHttpRequest();Uma única instância de XMLHttpRequest trata de uma requisição. Para fazer uma segunda requisição, crie um novo objeto.
Fazendo uma Requisição HTTP
Assim que o objeto existir, configure-o com open e, em seguida, dispare-o com send.
O Método open
open inicializa uma requisição, mas não a envia ainda. Ele recebe vários parâmetros:
xhr.open(method, url, async, user, password);method: O método HTTP a ser usado, por exemplo,'GET'ou'POST'.url: A URL para a qual a requisição é enviada.async: Um boolean que indica se a requisição é assíncrona. O padrão étrue, e você quase sempre deve deixá-lo assim (veja o aviso abaixo).user: Nome de usuário opcional para autenticação HTTP.password: Senha opcional para autenticação HTTP.
Exemplo:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);Requisições síncronas (xhr.open(method, url, false)) bloqueiam a página até a resposta chegar e estão depreciadas na thread principal. Mantenha sempre async definido como true.
O Método send
send despacha a requisição para o servidor. Todos os manipuladores de eventos devem ser anexados antes de chamá-lo. Para uma requisição GET, chame-o sem argumentos. Para um POST, passe o corpo da requisição como argumento.
Exemplo de uma requisição GET:
xhr.send();Exemplo de uma requisição POST com dados codificados como formulário:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('param1=value1¶m2=value2');O método setRequestHeader adiciona um cabeçalho HTTP à requisição de saída e deve ser chamado após open, mas antes de send.
Tratando Respostas do Servidor
Para tratar respostas do servidor, vários listeners de eventos podem ser usados.
O Evento onreadystatechange
O evento onreadystatechange é disparado sempre que a propriedade readyState muda. A propriedade readyState contém o status do XMLHttpRequest.
0: UNSENT1: OPENED2: HEADERS_RECEIVED3: LOADING4: DONE
Uma requisição só está concluída e bem-sucedida quando readyState é 4 (DONE) e o status HTTP está na faixa de sucesso (tipicamente 200). Verificar apenas readyState === 4 é um erro comum, pois o servidor pode ter respondido com 404 ou 500.
Exemplo:
Embora onreadystatechange funcione, o código moderno geralmente prefere onload e onerror para um tratamento de requisições mais simples e legível. onreadystatechange é usado principalmente quando você precisa rastrear estados intermediários (como progresso ou cabeçalhos recebidos).
O Evento load
O evento load é disparado assim que a resposta chega completamente. É mais simples que onreadystatechange, pois você não precisa testar readyState — ele só é disparado no estágio DONE. Você ainda verifica status para distinguir um sucesso real de um erro HTTP.
Exemplo:
O Evento progress
Para downloads grandes, você pode reportar o progresso com o evento progress. Quando o servidor envia um cabeçalho Content-Length, o evento é determinado (lengthComputable é true) e você pode calcular uma porcentagem:
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`Downloaded ${percent}%`);
}
};Para rastrear um upload, em vez disso, anexe manipuladores a xhr.upload (xhr.upload.onprogress). O objeto de progresso de upload é o recurso que o Fetch ainda não consegue replicar completamente.
Tratando Erros
Um código robusto deve tratar falhas. Dois eventos cobrem os casos de falha:
onerroré disparado em uma falha no nível de rede — a requisição nunca chegou ao servidor, o DNS falhou, o CORS a bloqueou, etc. Note que um HTTP404ou500não é um erro de rede: ele disparaload, nãoerror, portanto você ainda deve inspecionarstatus.ontimeouté disparado se a requisição demorar mais do quexhr.timeoutmilissegundos. Um timeout de0(o padrão) significa sem limite.
Exemplo:
Analisando Respostas JSON
As respostas do servidor são mais comumente JSON. A abordagem mais simples é ler o texto bruto de xhr.responseText e analisá-lo você mesmo com JSON.parse:
Alternativamente, defina xhr.responseType = 'json' antes de enviar, e o navegador analisará o corpo por você. O valor analisado estará disponível em xhr.response (não em 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 também aceita 'text', 'blob', 'arraybuffer' e 'document' para cargas úteis não JSON.
Enviando Dados com POST
Para enviar um corpo JSON, defina o cabeçalho Content-Type e serialize seu objeto com 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 envios de formulários tradicionais, envie um objeto FormData em vez disso — o navegador define o Content-Type multipart correto automaticamente, portanto não chame setRequestHeader para isso.
Cancelando uma Requisição
Chame xhr.abort() para cancelar uma requisição em andamento, por exemplo, quando o usuário navega para outra página ou digita uma nova consulta de pesquisa. Após o cancelamento, o evento abort é disparado em vez 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();O equivalente no Fetch usa um AbortController.
XMLHttpRequest vs Fetch
| XMLHttpRequest | Fetch | |
|---|---|---|
| Modelo de programação | Callbacks / eventos | Promessas, funciona com await |
| Progresso de upload | Sim (xhr.upload) | Não |
| Progresso de download | Sim (evento progress) | Via streams (mais código) |
| Cancelamento | xhr.abort() | AbortController |
| Rejeita em erro HTTP | Não, você verifica status | Não, você verifica response.ok |
Para a maioria dos novos códigos, prefira o Fetch. Recorra ao XHR quando precisar de relatórios granulares de progresso de upload ou quando for necessário suportar ambientes muito antigos.
Conclusão
XMLHttpRequest permite que o JavaScript troque dados com um servidor em segundo plano: você cria o objeto, abre-o com open, anexa manipuladores load/error/timeout e o envia com send. Lembre-se de verificar tanto readyState quanto status, analisar JSON você mesmo ou por meio de responseType, e usar abort() para cancelar requisições desatualizadas. Para a maioria dos novos códigos, a Fetch API baseada em promessas é o melhor padrão, mas entender o XHR mantém você fluente em bases de código mais antigas e nos casos — como o progresso de upload — em que ele ainda se sobressai.