JQuery - Utilizando Ajax - Entendendo o modo Assíncrono e Síncrono
As palavras síncrono e assíncrono podem soar estranhas quando aplicados ao mundo da TI. Serão mais dois dentre os tantos termos inventados por 'gurus' da web para vender livros?
É bem provável que, sim, que a adoção destes termos tenha sido feita por escritores ou editoras da área porém acredito que estas palavras são bem interessantes para descrever a real funcionalidade de tais sistemas. Utilizamos muito estes conceitos no javascript (como você já deve saber), mais especificamente na utilização do chamado Ajax (outro termo de gurus ?) .
Vamos tratar do assunto utilizando o serviço do Google Maps como exemplo, mais especificamente o serviço de busca de endereços.
Este serviço pode ser acessado utilizando-se a seguinte URL:
http://maps.googleapis.com/maps/api/geocode/json?address=endereco&sensor=false
Além disto vamos utilizar jQuery para fazer as requisições ajax e manitulação do DOM
Recomento fortemente a utilização Google Chrome (Firefox também pode ser utilizado sem problemas) mas isto acredito que isto você já faz pois não é possível que quem desenvolve para Web ainda utilize o IE :-)
Vamos ver na prática qual a diferença entre uma requisição assíncrona e uma síncrona e as maneiras como podemos tratar as informações retornadas.
Em primeiro lugar vamos ver como uma requisição assíncrona funciona:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
</head>
<body>
<div id="endereco">
</div>
</body>
<script type="text/javascript">
/**======= Busca =========*/
var busca =
{
// Propriedade de busca
info : '',
// Chama API Google Maps
googleapi: function(endereco) {
$.ajax({
'url' : 'http://maps.googleapis.com/maps/api/geocode/json?address='+encodeURIComponent(endereco)+'&sensor=false',
'async' : true,
'success' : function(data)
{
busca.info = data.results[0].formatted_address;
$('#endereco').html(busca.info);
}
});
}
};
/**======= Evento =========*/
$(document).ready(function(){
busca.googleapi('Av. Paulista 100, São Paulo');
});
</script>
</html>
O script acima vai fazer a consulta por Ajax na API do Google através do método 'busca.googleapi()' e retornar o endereço completo de 'Av Paulista 100, São Paulo'. Retornando o valor em 'busca.info' , utilizamos este para injetar no DOM do documento através de '$('#endereco').html(busca.info)'.
O script vai funcionar muito bem pois '$('#endereco').html(busca.info)' está dentro de 'success', que é iniciado quando o Ajax consegue retornar os valores da consulta, após alguns micro segundos.
Mas o que aconteceria se, por exemplo, mandássemos '$('#endereco').html(busca.info)' fora de 'success', ou mesmo fora do método 'busca.googleapi()' ?
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
</head>
<body>
<div id="endereco">
</div>
</body>
<script type="text/javascript">
/**======= Busca =========*/
var busca =
{
// Propriedade de busca
info : '',
// Chama API Google Maps
googleapi: function(endereco) {
$.ajax({
'url' : 'http://maps.googleapis.com/maps/api/geocode/json?address='+encodeURIComponent(endereco)+'&sensor=false',
'async' : true,
'success' : function(data)
{
busca.info = data.results[0].formatted_address;
}
});
return busca.info;
}
};
/**======= Evento =========*/
$(document).ready(function(){
var endereco = busca.googleapi('Av. Paulista 100, São Paulo');
$('#endereco').html(endereco);
});
</script>
</html>
Provavelmente o script acima não exibiu o endereço completo. Você faz noção do porquê?
Bem, o problema é que estamos injetando 'endereco', que é o retorno de 'busca.googleapi('Av. Paulista 100, São Paulo')' antes da resposta vinda da requisição Ajax, ou seja, o script roda mais rápido que a consulta aos servidores do Google, o que faz todo sentido.
Vamos confirmar isto exibindo a sequência de execução do script:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
</head>
<body>
<div id="endereco">
</div>
</body>
<script type="text/javascript">
/**======= Busca =========*/
var busca =
{
// Propriedade de busca
info : '',
// Chama API Google Maps
googleapi: function(endereco) {
$.ajax({
'url' : 'http://maps.googleapis.com/maps/api/geocode/json?address='+encodeURIComponent(endereco)+'&sensor=false',
'async' : true,
'success' : function(data)
{
busca.info = data.results[0].formatted_address;
console.log('Google API Retorno');
}
});
return busca.info;
}
};
/**======= Evento =========*/
$(document).ready(function(){
$('#endereco').html(busca.googleapi('Av. Paulista 100, São Paulo'));
console.log('Modificação do DOM');
});
</script>
</html>
Provavelmente você viu a seguinte sequência no Console de seu Chrome:
- Modificação do DOM
- Google API Retorno
Vemos que ele rodou primeiro o modificador e depois concluiu o retorno da consulta. O script é muito mais rápido que a consulta e, pela variável ainda estar vazia, ele imprime um vazio.
O script anterior funcionou simplesmente porque o modificador DOM estava em outro ponto do script, bem no método disparado após a consulta retornar seu valor e no ultimo caso o que o método disparado faz é apenas setar o valor na variável.
Se você digitar no console a seguinte linha:
busca.info
O endereço completo deve ser exibido.
Ou seja, a variável está com a informação, porém ela não chega a tempo de seguir a sequência do script e ser injetada no DOM. Percebemos assim que a sequência do programa é separada da consulta do Ajax, ou seja, as duas coisas ocorrem ao mesmo tempo (assíncrono).
Isto pode gerar muita confusão caso não se esteja acostumado a trabalhar com tarefas em paralelo. É comum o desenvolvedor reparar que os valores não estão sendo atualizados, ou que eles são atualizados apenas após uma segunda interação, justamente por causa disto, ou seja, o script funciona perfeitamente, só que as informações não chegam a tempo de serem exibidas. E lá se vão horas e horas até se perceber que o problema é tão somente a ordem em que as coisas ocorrem.
Bem, para resolvermos isto podemos setar a propriedade 'async' = false na chamada do Ajax. Isto fará com que todo o script do navegador 'pare' ou 'aguarde' até que a resposta seja retornada (síncrono), seja este retorno um dado ou um erro.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
</head>
<body>
<div id="endereco">
</div>
</body>
<script type="text/javascript">
/**======= Busca =========*/
var busca =
{
// Propriedade de busca
info : '',
// Chama API Google Maps
googleapi: function(endereco) {
$.ajax({
'url' : 'http://maps.googleapis.com/maps/api/geocode/json?address='+encodeURIComponent(endereco)+'&sensor=false',
'async' : false,
'success' : function(data)
{
busca.info = data.results[0].formatted_address;
console.log('Google API Retorno');
}
});
return busca.info;
}
};
/**======= Evento =========*/
$(document).ready(function(){
$('#endereco').html(busca.googleapi('Av. Paulista 100, São Paulo'));
console.log('Modificação do DOM');
});
</script>
</html>
Esta configuração deve trazer como resultado o endereço completo impresso na página.
Então, por estes exemplos, podemos ter uma noção do que são requisições assíncronas e síncronas.
A partir disto podemos decidir o que melhor fazer para nossas aplicações, organização e etc. Poderíamos, sim, colocar tudo dentro do método 'success' e pronto, porém quando você tem uma infinidade de outros métodos necessitando deste valor pode não ser interessante para a organização do script jogar tudo lá, pode ser mais interessante setar o Ajax síncrono e, depois de receber o valor, seguir o programa.
Você também pode colocar as chamadas dos seus outros métodos dentro do 'success' e assim pode continuar utilizando o processo assíncrono normalmente.
Utilizar o Ajax no modo síncrono pode parecer heresia para alguns mas cabe a você decidir a melhor forma de o utilizar. Lembre-se que deve sempre haver um equilíbrio entre a organização do script, para que possa ser de fácil manutenção, e a sua eficiência. Em aplicações críticas muitas vezes temos de sacrificar uma em detrimento da outra.
É isso. Abraço a todos!
Comentários
Postar um comentário