28 julho, 2011

Estratégia para lidar com callbacks assíncronos em Silverlight

Recentemente, conversando com alguns desenvolvedores no trabalho e verificando algumas perguntas publicadas nos fóruns do MSDN, eu notei que ainda há uma dificuldade muito grande tanto de compreensão quanto de implementação para trabalhar com métodos assincronos. Isso se torna um problema particularmente importante em aplicações Silverlight pois todas as chamadas a web services, RIA services, web requests, etc são obrigatoriamente feitos de forma assincrona, não havendo opções para executar essas mesmas operações da forma sincrona e linear à qual a maioria dos desenvolvedores está acostumada.

Acontece que desenvolvimento assincrono não é difícil e, depois que você aprende e se acostuma, você acaba percebendo que suas aplicações passam a funcionar muito melhor. Sim, não vou argumentar aqui contra o fato de que é necessário se acostumar e que começo seja realmente algo estranho, mas posso garantir que demora pouco tempo para se acostumar e os benefícios são muitos.

Há muitas abordagens e estratégias possíveis para desenvolvimento assíncrono e eu vou apresentar aqui uma delas que é bem simples e que eu usei em praticamente todos os projetos Silverlight em dos quais participei. Essa abordagem não envolve o uso de nenhum framework ou biblioteca externa e pode ser utilizada tranquilamente também em projetos que não sejam Silverlight.

Digamos que você precisa obter o html de uma página web por algum motivo. Uma forma de fazer isso seria criando uma nova instância de WebClient, assinando o evento DownloadStringCompleted e depois chamando o método DownloadString passando a url. Ok, não é difícil, mas é um código repetitivo que poderia facilmente ser reaproveitado ao invés de ser copiado por toda sua aplicação em todo lugar onde você precisar fazer download de uma página. O que eu costumo fazer para esse tipo de chamada é criar um método estático em uma classe utilitária e simplesmente chamar esse método passando, nesse caso, minha url e um ponteiro de callback. É mais fácil mostrar:

public static void HttpGet(string url, Action<string, Exception> callback) {
   
if (!string
.IsNullOrWhiteSpace(url)) {
       
var client = new WebClient
();
        client.DownloadStringCompleted += (sender, e) => {
           
if (callback != null
) {
                callback(e.Result, e.Error);
            }
        };
        client.DownloadStringAsync(
new Uri(url));
    }
}

Quais são as vantagens desse método:



  • para executá-lo não é necessário instanciar nenhuma classe
  • é facil de reutilizar
  • permite que a lógica da minha aplicação fique um pouco mais simples, já que não me obriga a assinar nenhum evento no meu código

Para executar esse método, eu posso usar 2 abordagens.


Abordagem 1 – Delegar o retorno para outro método. Nessa abordagem eu chamo o método HttpGet passando a url desejada e o ponteiro de um método que será executado quando o request for concluído.

private void LoadData() {
    HttpGet(
"http://kelps.net"
, DataLoaded);
}

private void DataLoaded(string data, Exception
error) {
   
if (error == null
) {
       
//utiliza os dados retornados na variável "data"
    }
}

Abordagem 2 – Utilizar uma expressão lambda para criar um método anônimo inline no meu código, ao invés de criar uma função separada apenas para processar os dados retornados.

HttpGet("http://twitter.com/kelps", (data, error) => {
   
if (error == null
) {
       
//utiliza os dados retornados na variável "data"
    }
});

A única diferença de funcionamento entre as 2 abordagens acima é que na segunda seria possível utilizar variáveis que estiverem no mesmo escopo da chamada que está sendo feita, ao passo que na primeira seria necessário que essas variáveis fossem globais da classe para que isso funcione. Nos projetos em que trabalho eu costumo utilizar ambas as abordagens, de acordo com o que faz mais sentido em cada situação. Expressões lambda são bem concisas e compactas, mas são claras para qualquer desenvolvedor.


Este foi apenas um pequeno exemplo de como trabalhar com chamadas assincronas sem ficar se perdendo com assinaturas e liberação de eventos. Há outras formas mais complexas e robustas de lidar com isso mas a minha intenção hoje era simplesmente mostrar como dá pra trabalhar de forma simples com código assíncrono, mesmo sem utilizar nenhuma biblioteca externa.

Um comentário:

Alexandre Nascimento disse...

bom post, isso realmente eh bem confuso no começo pelo que gerwlmente estamos acostumados a fazer. Abs!