A Programação Reativa pode ser considerada uma programação funcional com esteroides.

Por Matthew Tyson
A programação Reativa representa uma faceta importante do desenvolvimento moderno de software. Baseia-se na visão do software como uma série de interações que ocorrem entre produtores, consumidores e modificadores de eventos.
Aplicada corretamente, esta abordagem de desenvolvimento de software oferece vantagens significativas quando se trata de montar, alargar e tornar o código compreensível.
A programação Reativa pode ajudar a otimizar as arquiteturas das suas aplicações numa série de cenários. Por exemplo, nos domínios de:
- Processamento de dados em tempo real,
- programação assíncrona,
- interfaces de utilizador e
- sistemas distribuídos.
Programação Reativa – Definição
A Programação Reativa é um paradigma de desenvolvimento de software que modela entradas e saídas como fluxos de eventos. Utilizando esta abordagem, os programadores podem criar aplicações num estilo declarativo, funcional e normalizado.
Os fluxos de eventos estão no centro desta abordagem: podem representar qualquer tipo de fluxo de dados, desde a atividade da rede até às linhas de um ficheiro de texto. Sempre que uma fonte de dados pode desencadear eventos discretos com base em elementos dos dados, pode ser empacotada ou modelada como um fluxo reativo.
Com os fluxos de eventos, é possível ligar fontes de entrada e saída como elementos compostáveis e manipulá-los simultaneamente em qualquer ponto da cadeia. Desta forma, as aplicações tornam-se uma rede de fluxos – com produtores, consumidores e modificadores.
Na prática, a programação Reativa é semelhante à programação funcional, ou inspirada por esta. De seguida, veja um pequeno exemplo de um programa reativo em JavaScript:
// Criar um fluxo de eventos a partir de uma tecla de entrada de texto
const input = document.getElementById(‘myInput’);
const keypressStream = rxjs.fromEvent(input, ‘keypress’);
// Aplicar operadores ao fluxo
const filteredStream = keypressStream.pipe(
rxjs.operators.map(event => event.key),
rxjs.operators.filter(key => key !== ‘ ‘),
rxjs.operators.throttleTime(500) // Limita os eventos de premir teclas a um máximo de um evento por 500ms
);
// Subscrever o fluxo e tratar os eventos
filteredStream.subscribe(key => {
console.log(‘Keypress:’, key);
});
Esse código monitora uma entrada de texto para pressionamentos de tecla e usa a biblioteca JavaScript reativa RxJS para transformá-los em um fluxo de eventos. Para fazer isso, ele usa os operadores reativos map, filter e throttleTime. Também eliminam espaços no código e limitam a frequência do evento a 500 milissegundos. Por fim, é criada uma subscrição para o fluxo, que envia os eventos convertidos para a consola.
Programação Reativa – Vantagens e desvantagens
Os fluxos de eventos são um meio claro e portátil de representar fluxos de dados. Ao utilizar fluxos de eventos, o código torna-se declarativo em vez de imperativo, o que pode ser uma grande vantagem em termos de composição e compreensão. Além disso, os sistemas reativos são concebidos para processar fluxos de dados de forma assíncrona. Por um lado, isto torna-os escaláveis e, por outro, otimizáveis em termos de concorrência.
O reverso da medalha: a programação Reativa exige muita aprendizagem – existem, portanto, poucos verdadeiros especialistas neste domínio. Outra desvantagem: uma vez compreendido o poder da abordagem Reativa, pode haver a tentação de a considerar uma panaceia.
Programação Reativa – Elementos
Num nível elevado, a Programação Reativa baseia-se em vários elementos fundamentais:
Os observáveis são o meio concreto de modelar fluxos de eventos arbitrários como componentes reutilizáveis. Os observáveis têm uma API bem definida que gera eventos, erros e eventos de ciclo de vida que outros componentes podem subscrever ou modificar. Essencialmente, o tipo observável transforma um fluxo de eventos em uma entidade portátil e programável.
Os observadores são a contraparte dos observáveis. Eles se inscrevem e participam dos eventos que os Observáveis geram. Como resultado, o código da aplicação tende a assumir uma postura de inversão de controlo, combinando Observáveis e Observadores em vez de lidar diretamente com a funcionalidade.
Os operadores são o equivalente aos operadores funcionais, também conhecidos como funções de ordem superior. Os operadores reativos permitem que os eventos que passam pelo fluxo sejam manipulados de várias formas. Mais uma vez, o código da aplicação pode permanecer à distância, injetando a funcionalidade desejada nos fluxos “a partir de cima” – enquanto as operações permanecem intimamente ligadas aos dados que processam.
Agendamento
Outro tipo de componente importante é o agendador. Na programação Reativa, ajuda a gerir a forma como os eventos são tratados pelo motor. Por exemplo, no nosso exemplo dado no início, poderíamos instruir o motor para executar a operação com um agendador assíncrono:
const filteredStream = keypressStream.pipe( rxjs.operators.observeOn(rxjs.asyncScheduler), rxjs.operators.map(event => event.key), rxjs.operators.filter(key => key !== ‘ ‘), rxjs.operators.throttleTime(500) );
O conceito de um agendador é uma forma poderosa de definir e controlar o comportamento dos fluxos. Existem diversas variações em diferentes frameworks. Além disso, também é possível escrever suas próprias implementações.
Contrapressão
Outro conceito importante na Programação Reativa é o Backpressure. Essencialmente, ele responde à pergunta: O que acontece quando muitos eventos ocorrem muito rapidamente para o sistema lidar?
Uma estratégia de contrapressão ajuda a gerir o fluxo de dados entre produtores e consumidores de eventos e a garantir que estes últimos conseguem lidar com o volume de eventos recebidos sem ficarem sobrecarregados. Existem várias abordagens gerais para o efeito – por exemplo:
Eliminação: Quando os eventos se acumulam, são “eliminados”. Assim que o consumidor é capaz de processar mais, os eventos mais recentes são entregues. O foco aqui é a vivacidade.
Buffering: É criada uma fila de eventos não processados que é gradualmente entregue ao consumidor assim que este os consegue processar. O foco: consistência.
Limitação: A taxa de entrega de eventos é reduzida através de várias estratégias, como a limitação do tempo, a limitação da contagem ou a utilização de tokens.
Sinalização: É criada uma forma de comunicar o status quo da contrapressão ao produtor de eventos para que este possa reagir em conformidade.
As estratégias de contrapressão podem mudar dinamicamente de acordo com as condições. Se quiséssemos alargar o nosso exemplo de modo a incluir o tratamento da contrapressão de um buffer baseado na contagem, funcionaria da seguinte forma
const filteredStream = keypressStream.pipe(
rxjs.operators.throttleTime(500), // Limita os eventos de pressionamento de tecla a um máximo de um evento por 500ms
rxjs.operators.observeOn(rxjs.asyncScheduler),
rxjs.operators.map(event => event.key),
rxjs.operators.filter(key => key !== ‘ ‘),
rxjs.operators.bufferCount(5)
);
É claro que a contrapressão não é necessária para este exemplo, mas dá uma ideia de como funciona. O número de toques no teclado é limitado a 500 milissegundos. Assim que cinco desses toques de tecla forem feitos, são encaminhados para o subscritor para serem emitidos na consola. Pode ver este exemplo em direto aqui.
Estruturas reativas
No campo da Programação Reativa, existem inúmeras frameworks e motores para várias linguagens e plataformas. O principal projeto nesta área é o ReactiveX, cujas especificações-padrão são implementadas em todas as principais linguagens de programação.
Além disso, existem outras estruturas Reativas – por exemplo
- as Extensões Reativas do .NET,
- o projeto Reator (baseado na JVM),
- Spring WebFlux (baseado no Reator),
- a estrutura Play (Java, Scala),
- Vert.x (para Java, Kotlin e Groovy),
- Akka Streams (Java),
- Trio (Python) ou
- Nito.AsynxExec (.Net).
Algumas linguagens também têm um bom suporte reativo integrado. Go com goroutines é apenas um exemplo.