Mantendo os aplicativos rápidos e fluidos com a assincronia no Tempo de Execução do Windows

Blog do desenvolvedor de aplicativos do Windows 8

Ideias sobre a criação de aplicativos com o estilo Metro para o Windows 8, da equipe de engenharia do Windows 8

Mantendo os aplicativos rápidos e fluidos com a assincronia no Tempo de Execução do Windows

  • Comments 0

Os seres humanos são assíncronos por natureza, e isso afeta diretamente a maneira como esperamos que os aplicativos nos respondam. O WinRT (Tempo de Execução do Windows) acolheu essa assincronia como um cidadão de primeira classe na criação de aplicativos estilo Metro rápidos e fluidos. Se você estiver criando um aplicativo estilo Metro, chegará uma hora em que terá de escrever alguns códigos assíncronos. Nesta postagem, falamos sobre por que a programação assíncrona é tão prevalente no WinRT e oferecemos algumas noções básicas sobre como usá-la nos seus aplicativos e algumas informações sobre como ela funciona.

Aplicativos rápidos e fluidos devem ter uma grande capacidade de resposta

Quantas vezes um aplicativo do Windows parou de responder e a tela ficou cinza, exibindo o anel girando? Parece que isso sempre acontece na pior hora possível. E o pior é que você pode acabar perdendo muito trabalho quando isso acontece.

Os usuários esperam que os aplicativos tenham uma grande capacidade de resposta, em todas as interações. Ao usar o aplicativo de leitura de notícias favorito, eles desejam adicionar news feeds, ler e salvar artigos de notícias etc. Eles precisam poder fazer isso tudo mesmo quando o aplicativo estiver recuperando os últimos artigos da Internet.

Isso se torna ainda mais importante quando o usuário interage com o seu aplicativo usando o toque. Os usuários percebem quando o aplicativo não "obedece" ao seu dedo. Até mesmo pequenos problemas de desempenho podem prejudicar a experiência "rápida e fluida".

Um aplicativo não é rápido e fluido se ele para de responder às entradas do usuário. Mas por que os aplicativos param de responder? Uma das principais causas é o fato de o aplicativo ser síncrono; ele fica esperando a conclusão de alguma outra ação (como a obtenção de dados da Internet) e não pode responder à sua entrada.

Muitos aplicativos modernos se conectam a sites de redes sociais, armazenam dados na nuvem, funcionam com arquivos do disco rígido, se comunicam com outros gadgets e dispositivos etc. Algumas dessas fontes apresentam latências imprevisíveis, o que torna a criação de aplicativos rápidos e fluidos um desafio. A menos que criados de forma correta, os aplicativos levam mais tempo aguardando ações do ambiente externo do que respondendo às necessidades do usuário.

Enfocar esse mundo conectado foi um dos princípios básicos quando começamos a criar APIs para o WinRT (Tempo de Execução do Windows). Achamos que era importante oferecer uma superfície de API que fosse avançada e permitisse aplicativos rápidos e fluidos automaticamente.

Para atingir essas metas, tornamos assíncronas muitas APIs possivelmente I/O-bound no Tempo de Execução do Windows. Elas são as candidatas mais prováveis a prejudicar o desempenho de forma visível quando escritas de maneira síncrona (por exemplo, a execução poderia levar mais de 50 milissegundos). Essa abordagem assíncrona para APIs permite que você escreva código rápido e fluido por padrão e promove a importância da capacidade de resposta dos aplicativos no desenvolvimento de aplicativos estilo Metro.

Para aqueles não familiarizados com os padrões assíncronos, pense na assincronia do WinRT como quando damos um número para que alguém retorne o nosso telefonema depois. Você fornece um número para retorno de chamada para a pessoa, desliga o telefone e continua fazendo qualquer outra coisa que precise fazer. Quando a pessoa fica disponível para falar com você, ela pode usar o número fornecido para retornar a chamada. É basicamente assim que a assincronia funciona no WinRT.

Para você entender melhor a natureza assíncrona do WinRT, vamos ver primeiro como é possível usar essas APIs assíncronas de forma simples. Depois, vamos olhar os bastidores das primitivas de assincronia introduzidos pelo WinRT (sobre os quais os novos recursos de linguagem são construídos) e como eles funcionam.

Como usar: novas melhorias na assincronia para o desenvolvedor

Antes, era difícil escrever código assíncrono. As ferramentas de criação de aplicativos estilo Metro do Windows 8 das nossas equipes permitiram avanços significativos na solução desse problema nas versões recentes. No .NET Framework 4.0, introduzimos a Biblioteca paralela de tarefas. A palavra-chave await foi introduzida no C# e no Visual Basic. O C++ investiu em tecnologias como a PPL (Biblioteca paralela de padrões). E o JavaScript tem ótimas ferramentas como o Promises.

Cada uma dessas tecnologias apresenta uma forma simples de escrever código assíncrono. Com a introdução de um padrão assíncrono básico usado por todas essas tecnologias, você pode usá-las ao criar aplicativos estilo Metro, independentemente da linguagem de programação que estiver usando.

No caso do seu aplicativo de notícias favorito, o código que mantém a grande capacidade de resposta enquanto recupera artigos de notícias é muito simples com o uso da API Windows.Web.Syndication.SyndicationClient. A chamada para o RetrieveFeedAsync é retornada imediatamente. Isso é ótimo porque os resultados podem levar muito tempo para retornar da Internet. Enquanto isso, a interface continua interagindo com o usuário.

O código realçado em amarelo é executado depois que a chamada para o RetrieveFeedAsync é concluída. Você não precisa pensar em todas as conexões para "recomeçar de novo de onde parou". E o código é escrito de maneira simples, como se fosse síncrono.

Código JavaScript para recuperar RSS feed de forma assíncrona:
var title;
var feedUri = new Windows.Foundation.Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();

client.retrieveFeedAsync(feedUri).done(function(feed) {
title = feed.title.text;
});
 
Código C# para recuperar RSS feed de forma assíncrona:
var feedUri = new Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();
var feed = await client.RetrieveFeedAsync(feedUri);
var title = feed.Title.Text;

Código VB para recuperar RSS feed de forma assíncrona:
Dim feedUri = New Uri("http://www.devhawk.com/rss.xml");
Dim client = New Windows.Web.Syndication.SyndicationClient();
Dim feed = Await client.RetrieveFeedAsync(feedUri);
Dim title = feed.Title.Text;
 

Código C++ para recuperar RSS feed de forma assíncrona:
auto feedUri = ref new Windows::Foundation::Uri("http://www.devhawk.com/rss.xml");
auto client = ref new Windows::Web::Syndication::SyndicationClient();

task<SyndicationFeed^> retrieveTask(client->RetrieveFeedAsync(feedUri));
retrieveTask.then([this] (SyndicationFeed^ feed) {
this->title = feed->Title->Text;
});

 

Nem falamos em threads, troca de contexto, dispatchers etc. Essas novas melhorias do desenvolvedor para escrever código assíncrono tratarão disso para você. O código após a chamada à API assíncrona é retornado automaticamente no mesmo contexto em que é feita a chamada original. Isso significa que você pode atualizar a interface do usuário com os resultados sem se preocupar em retornar ao thread da interface do usuário.

Como usar: tratamento de erro

É claro que pode haver falha nas chamadas à API (como perder a conexão de rede ao recuperar RSS feeds). Para que o processo seja realmente robusto, precisamos ser resilientes na presença de erros. O uso da funcionalidade de assincronia nas linguagens torna o tratamento de erros mais simples. O mecanismo para isso varia de acordo com a linguagem.

No JavaScript, recomendamos que você sempre termine as suas cadeias do Promises com done. Isso garante que toda exceção capturada na cadeia do Promises possa ser vista pelo desenvolvedor (isto é, reportada no manipulador de erros ou emitida). O done possui a mesma assinatura do then. Portanto, para tratar erros, basta transmitir um delegado de erro em done():

 

var title;
var feedUri = new Windows.Foundation.Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();

client.retrieveFeedAsync(feedUri).done(function(feed) {
title = feed.title.text;
}, function(error) {
console.log('an error occurred: ');
console.dir(error);
}
);

Para tratar a exceção no C# ou Visual Basic, basta usar um bloco try/catch, exatamente como hoje fazemos com o código síncrono:

 

var title;
var feedUri = new Uri("http://www.devhawk.com/rss.xml");
var client = new Windows.Web.Syndication.SyndicationClient();

try
{

var feed = await client.RetrieveFeedAsync(feedUri);
title = feed.Title.Text;
}
catch (Exception ex)
{
// An exception occurred from the async operation
}

O método mais comum a ser usado no C++ para tratar erros de assincronia é usar outra continuação baseada em tarefas que emite as exceções (veja outros métodos de tratamento de erros no C++ no tópico Asynchronous Programming in C++ (Programação assíncrona no C++) no Centro de Desenvolvimento):

auto feedUri = ref new Windows::Foundation::Uri("http://www.devhawk.com/rss.xml");
auto client = ref new Windows::Web::Syndication::SyndicationClient();

task<SyndicationFeed^> retrieveTask(client->RetrieveFeedAsync(feedUri));
retrieveTask.then([this] (SyndicationFeed^ feed) {
this->title = feed->Title->Text;
}).then([] (task<void> t) {
try
{
t.get();
// .get() didn't throw, so we succeeded
}
catch (Exception^ ex)
{
// An error occurred
}
})
;

Para obter mais detalhes sobre como usar esses aperfeiçoamentos de assincronia (incluindo outros cenários como o progresso e cancelamento do suporte:

Como funciona: Primitivas de assincronia do WinRT

A assincronia é muito avançada, mas pode parecer que tem muita mágica por trás. Para desmistificar o que acontece, vamos ver mais detalhadamente como funciona a assincronia no WinRT. O nosso primeiro passo é investigar as primitivas nas quais o modelo é criado. A maioria de vocês nunca precisará usar essas primitivas (se você souber de um cenário em que elas sejam necessárias, vamos adorar ouvir os seus comentários).

Começando com o código C#, vamos ver o tipo de retorno real do RetrieveFeedAsync (mostrado aqui em C# Intellisense):

 Exibição do Intellisense para o método do RetrieveFeedAsync
 Figura 1. Exibição do Intellisense para o método do RetrieveFeedAsync

RetrieveFeedAsync retorna uma interface IAsyncOperationWithProgress. IAsyncOperationWithProgress é uma das 5 interfaces que definem a superfície de programação assíncrona básica do WinRT: IAsyncInfo, IAsyncAction, IAsyncActionWithProgress, IAsyncOperation e IAsyncOperationWithProgress.

A interface básica em que é criado o modelo de assincronia do WinRT é IAsyncInfo. Essa interface básica define as propriedades de uma operação assíncrona, como o status atual, a capacidade de cancelar a operação, o erro de uma operação com falha etc.

Como mencionamos antes, as operações assíncronas podem retornar resultados. As operações assíncronas no WinRT também podem relatar o andamento enquanto são executadas. As outras 4 interfaces de assincronia acima (IAsyncAction, IAsyncActionWithProgress, IAsyncOperation e IAsyncOperationWithProgress) definem diferentes combinações de resultados e andamento.

 

Tabela mostrando diferentes combinações de resultados e andamento   
Figura 2. Diferentes combinações de resultados e andamento

As primitivas de assincronia básicas no Windows Runtime permitem que o C#, Visual Basic, C++ e JavaScript projetem operações assíncronas do WinRT de maneira familiar.

Observação: A mudança da inicialização a frio para a inicialização a quente

No Windows 8 Developer Preview lançado na //build, todas as operações assíncronas no WinRT tinham uma inicialização a frio. Elas não começavam a ser executadas imediatamente. Elas tinham de ser iniciadas explicitamente pelo desenvolvedor ou implicitamente com o uso de recursos de assincronia no C#, Visual Basic, JavaScript ou C++. Recebemos comentários de várias fontes que diziam que isso não era intuitivo e poderia causar problemas e confusão.

No lançamento do Windows 8 Consumer Preview, você observará que removemos o método Start() das primitivas de assincronia do WinRT. Agora, todas as operações assíncronas têm inicialização a quente. Portanto, o método de assincronia inicia a operação antes de retorná-la para o chamador.

Essa é apenas uma das muitas alterações que você verá no Consumer Preview, cuja plataforma de desenvolvedor foi ajustada com base em todos os ótimos comentários que recebemos de vocês.

Como funciona: resultados, cancelamento e erros com primitivas de assincronia

Uma operação assíncrona é iniciada no estado Started e pode passar para um destes três outros estados: Canceled, Completed e Error. O estado atual de uma operação assíncrona é refletido na propriedade Status da operação assíncrona (representado pelo tipo de enum AsyncStatus).

A primeira coisa que fazemos com uma operação assíncrona é conectar um manipulador concluído. Do manipulador concluído, podemos obter e usar os resultados.

IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> op;
op = client.RetrieveFeedAsync(feedUri);

op.Completed = (info, status) =>
{
SyndicationFeed feed = info.GetResults();
UpdateAppWithFeed(feed);
};

Você pode desejar cancelar uma operação. Isso pode ser feito chamando o método Cancel na operação assíncrona.

IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> op;
op = client.RetrieveFeedAsync(feedUri);
op.Cancel();

O manipulador Completed sempre é chamado para uma operação assíncrona, independentemente de ela ter sido concluída, cancelada ou resultado em erro. Chame GetResults somente quando a operação assíncrona tiver sido concluída (não quando a operação tiver sido cancelada ou terminada com erro, por exemplo). Você pode determinar isso inspecionando o parâmetro de status.

IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> op;
op = client.RetrieveFeedAsync(feedUri);
op.Cancel();

op.Completed = (info, status) =>
{
if (status == AsyncStatus.Completed)
{
SyndicationFeed feed = info.GetResults();
UpdateAppWithFeed(feed);
}
else if (status == AsyncStatus.Canceled)
{
// Operation canceled
}
};

Esse código ainda não é robusto se a operação tiver falhado. Assim como no cancelamento, há suporte ao relatório de erros por meio da mesma operação assíncrona. Além de usarmos o AsyncStatus para diferenciar entre Completed e Canceled, ele também é usado para determinar a falha de uma operação assíncrona (isto é, AsyncStatus.Error). Você pode localizar o código de falha específico na propriedade ErrorCode da operação assíncrona.

op.Completed = (info, status) =>
{
if (status == AsyncStatus.Completed)
{
SyndicationFeed feed = info.GetResults();
UpdateAppWithFeed(feed);
}
else if (status == AsyncStatus.Canceled)
{
// Operation canceled
}
else if (status == AsyncStatus.Error)
{
// Error occurred, Report error
}
};

Como funciona: relatando o andamento com primitivas de assincronia

Algumas operações assíncronas no WinRT oferecem notificações sobre o andamento enquanto são executadas. Você pode usar essas notificações para relatar o andamento atual da operação assíncrona para o usuário.

No WinRT, o relatório do andamento é manipulado por meio das interfaces IAsyncActionWithProgress<TProgress> e IAsyncOperationWithProgress<TResult, TProgress>. Cada interface contém um evento de andamento que você pode usar para receber relatórios de andamento da operação assíncrona.

O modelo de programação para consumir isso é praticamente idêntico ao consumo do evento concluído. No nosso exemplo de recuperação de feeds de sindicalização, podemos relatar quantos bytes do valor total foram recuperados:

 

op = client.RetrieveFeedAsync(feedUri);

float percentComplete = 0;
op.Progress = (info, progress) =>
{
percentComplete = progress.BytesRetrieved /
(float)progress.TotalBytesToRetrieve;
};

Nesse caso, o segundo parâmetro (progress) é do tipo RetrievalProgress. Esse tipo contém dois parâmetros (bytesRetrieved e totalBytesToRetrieve) que são usados para relatar o nosso andamento atual.

Para consultar mais materiais (sobre programação assíncrona no WinRT ou assincronia em geral), confira:

Resumo

O que é interessante sobre a prevalência da assincronia no WinRT é como ela impacta a maneira como você estrutura o seu código e arquiteta o seu aplicativo. Você começa a pensar no seu aplicativo de uma forma mais flexível. Se tiver algum comentário a fazer sobre o assunto, vamos adorar ouvi-lo! Deixe um comentário aqui ou nos fóruns do Centro de Desenvolvimento.

Desejamos que os seus clientes adorem os seus aplicativos do Windows 8. Desejamos que esses aplicativos comecem dinâmicos com muitas atividades e assim permaneçam. E queremos que você crie os aplicativos mais facilmente. Desejamos que você possa facilmente se conectar a sites de redes sociais, armazenar dados na nuvem, trabalhar com arquivos do disco rígido, se comunicar com outros gadgets e dispositivos etc. proporcionando, ao mesmo tempo, aos seus clientes, uma ótima experiência do usuário.

 

--Jason Olson

Gerente de programas, Windows

  • Loading...
Leave a Comment
  • Please add 2 and 7 and type the answer here:
  • Post