Ajax (parte 2): encarando o mundo real

XmlHTTPRequest e Threads

Há duas maneiras de se fazer uma requisição com um objeto XMLHttpRequest, uma é síncrona, outra assíncrona. (Se você é um programador com experiência em threads, provavelmente já entendeu o que eu quis dizer e pode pular este e o próximo parágrafos.) No modo síncrono, quando você manda o objeto fazer uma requisição, o seu script é interrompido esperando pelo retorno. No modo assíncrono a requisição é feita em segundo plano (no jargão técnico, dizemos que ela é feita em outra thread) e seu script continua a ser executado.

Há um grave problema em trabalhar com o objeto XMLHttpRequest em modo síncrono, que é o fato de seu navegador permanecer congelado enquanto seu script é executado. O que acontece se um script demora muito para terminar? Primeiro, seu navegador congela, como você pode ver clicando aqui. Em meu computador o link anterior leva cerca de 3 segundos e meio para ser executado no Firefox, 2 segundos no IE e mais de 10 segundos no Konqueror, e durante esse período não posso sequer rolar a tela. O segundo efeito colateral é que, se um script realmente demora muito tempo, o navegador pergunta a você se quer interromper o script. É o que acontece comigo aqui no Konqueror, e deve estar acontecendo aí se você tem uma máquina bem mais lenta que a minha. Se sua máquina é rápida demais para que você possa ver este aviso, tente clicando nesse outro link. Isso é muito perigoso quando se trata de XMLHttpRequest, uma vez que o tempo para a requisição feita depende da sorte. Se o servidor estiver sobrecarregado, ou a internet congestionada, ou o modem do usuário com problemas, ou por qualquer outro motivo que depende dos humores do cobre e do silício, você trava o navegador do usuário. Isso é visto como algo muito antipático, e nós não queremos isso, não é mesmo.

Para escolher de que forma o objeto XMLHttpRequest vai trabalhar, usamos o terceiro parâmetro do método open. No nosso código do primeiro artigo:

//Abre a url
xmlhttp.open("GET", "funcoes.php?n="+n,true)

Esse true, no terceiro parâmetro, coloca o objeto em modo assíncrono. Ao fazer a requisição o objeto vai executar a função onreadystatechange, que criamos assim:

xmlhttp.onreadystatechange=function(){
    if(xmlhttp.readyState==4){
        /*Faz o que tem que fazer aqui*/
    }
}

Esse código vai ser executado várias vezes durante a requisição, por isso testamos readyState. Quando readyState é 4, isso significa que a requisição foi concluída e podemos ler o retorno e fazer o que precisarmos com ele. Problemas vão acontecer se tentarmos executar duas requisições, assim:

function atualizaDoisLugaresDiferentes(){

    xmlhttp.open("GET","url1.php",true)
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState==4){
            /*Atualiza o lugar 1*/
        }
    }
    xmlhttp.send(null)

    xmlhttp.open("GET","url2.php",true)
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState==4){
            /*Atualiza o lugar 2*/
        }
    }
    xmlhttp.send(null)

}

Ao executar a primeira vez o método send nosso script não vai ficar esperando pelo objeto XMLHttpRequest, ele continua sendo executado. A linha seguinte é uma chamada ao método open do mesmo objeto, que está ocupado com nossa primeira requisição. Os resultados são imprevisíveis, principalmente em navegadores diferentes, mas certamente isso não vai funcionar direito em nenhum deles.

As soluções são criar mais de um objeto XMLHttpRequest ou criar uma fila de requisições. A primeira solução pode parecer mais simples, mas ela terá que ser reescrita para cada aplicação. Além disso, ter vários objetos de requisição carregados no navegador pode ser uma fonte de problemas, principalmente se alguém resolver visitar seu site usando um velho e corajoso Pentium 233. Vamos então trabalhar numa fila de conexões, que possa ser reutilizada em vários projetos.

Elcio Ferreira * Tableless.com.br