<h2>Fundamentos da Programação Reativa</h2>
<p>A programação reativa é um paradigma que trata com fluxos de dados assíncronos e a propagação de mudanças. Em vez de estruturar código imperativo que diz "faça isso, depois aquilo", você declara "quando isso muda, aquilo acontece automaticamente". Esse modelo é especialmente poderoso em aplicações modernas onde eventos, requisições HTTP e interações do usuário ocorrem continuamente e de forma impredizível.</p>
<p>O conceito central é o de <strong>Observable</strong> — um objeto que representa uma sequência de valores que podem ser emitidos ao longo do tempo. Pense em um Observable como uma TV transmitindo canais: múltiplos espectadores (observadores) recebem os mesmos dados simultaneamente. Essa abordagem simplifica código assincronamente complexo, reduz callbacks aninhados (callback hell) e torna o tratamento de erros mais elegante e centralizado.</p>
<h2>RxJS: Implementação Prática em JavaScript</h2>
<p>RxJS (Reactive Extensions for JavaScript) é a biblioteca padrão para programação reativa em JavaScript. Ela fornece a implementação de Observables, Observers e Operators — funções que transformam e combinam fluxos de dados. O RxJS integra-se perfeitamente com frameworks como Angular e é amplamente utilizado em aplicações React também.</p>
<pre><code class="language-javascript">const { of, interval, from } = require('rxjs');
const { map, filter, take } = require('rxjs/operators');
// Exemplo 1: Observable simples com 'of'
of(1, 2, 3, 4, 5)
.pipe(
filter(x => x % 2 === 0),
map(x => x * 10)
)
.subscribe(
value => console.log('Valor:', value),
error => console.error('Erro:', error),
() => console.log('Completo!')
);
// Output: Valor: 20, Valor: 40, Completo!
// Exemplo 2: Observable contínuo com 'interval'
interval(1000)
.pipe(
take(3)
)
.subscribe(value => console.log('Segundo:', value));
// Output: Segundo: 0, Segundo: 1, Segundo: 2 (a cada segundo)</code></pre>
<p>A sintaxe <code>pipe()</code> é fundamental — permite encadear múltiplos operadores, transformando o fluxo de dados em cada etapa. O <code>subscribe()</code> é onde você define o que fazer com os valores emitidos, erros capturados ou quando o fluxo termina (três callbacks opcionais, nesta ordem).</p>
<h2>Operadores Essenciais e Casos de Uso Reais</h2>
<h3>Operadores Mais Utilizados</h3>
<p>RxJS possui mais de 100 operadores. Os mais essenciais para iniciantes são: <code>map()</code> (transforma dados), <code>filter()</code> (seleciona apenas valores que atendem condição), <code>mergeMap()</code> e <code>switchMap()</code> (combinam múltiplos Observables), <code>debounceTime()</code> (aguarda pausa antes de emitir) e <code>catchError()</code> (trata erros).</p>
<pre><code class="language-javascript">const { fromEvent } = require('rxjs');
const { debounceTime, map, switchMap } = require('rxjs/operators');
const fetch = require('node-fetch');
// Simulando busca em tempo real (autocomplete)
// Em um navegador real, seria: fromEvent(inputElement, 'input')
const searchInput$ = fromEvent(document.querySelector('#search'), 'input');
searchInput$
.pipe(
debounceTime(300), // Aguarda 300ms de inatividade
map(event => event.target.value),
filter(term => term.length > 2),
switchMap(term =>
fetch(https://api.example.com/search?q=${term})
.then(res => res.json())
),
catchError(error => {
console.error('Erro na busca:', error);
return of([]); // Retorna array vazio em caso de erro
})
)
.subscribe(results => console.log('Resultados:', results));</code></pre>
<p>Este exemplo é típico do mundo real: usuário digita em um campo, você quer fazer requisição HTTP, mas não a cada caractere (custoso). Com <code>debounceTime()</code> e <code>switchMap()</code>, você aguarda o usuário parar de digitar 300ms e depois dispara uma única requisição, cancelando automaticamente qualquer requisição anterior pendente.</p>
<h2>Tratamento de Erros e Unsubscribe</h2>
<h3>Gerenciamento de Memória</h3>
<p>Todo Observable que você se inscreve (subscribe) mantém uma conexão na memória. Em aplicações web, especialmente em Single Page Applications (SPAs), é crítico desinscrever quando componentes são destruídos. Caso contrário, você terá vazamento de memória.</p>
<pre><code class="language-javascript">const { Subject, Subscription } = require('rxjs');
const { takeUntil } = require('rxjs/operators');
class MinhaClasse {
private destroy$ = new Subject<void>();
private subscriptions: Subscription[] = [];
// Forma 1: Usando takeUntil (recomendado)
subscribe() {
this.minhaFuncao$
.pipe(
takeUntil(this.destroy$)
)
.subscribe(value => console.log(value));
}
// Forma 2: Armazenando subscriptions manualmente
subscribeDirect() {
const sub = this.minhaFuncao$
.subscribe(value => console.log(value));
this.subscriptions.push(sub);
}
ngOnDestroy() {
// Método chamado quando o componente Angular é destruído
this.destroy$.next();
this.destroy$.complete();
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}</code></pre>
<p>O padrão <code>takeUntil()</code> é elegante: você cria um Subject que atua como "gatilho de destruição". Quando seu componente morre, você emite um valor no <code>destroy$</code>, e todos os Observables inscritos com esse operador se desinscreverão automaticamente. Em Angular, o framework moderno oferece o <code>async</code> pipe que gerencia isso para você automaticamente.</p>
<pre><code class="language-javascript">// Tratamento de erros com retry
const { retry } = require('rxjs/operators');
minhaRequisicaoAPI$
.pipe(
retry(3), // Tenta novamente até 3 vezes se falhar
catchError(error => {
console.log('Falhou após 3 tentativas:', error);
return of(null);
})
)
.subscribe(data => processarDados(data));</code></pre>
<h2>Conclusão</h2>
<p>Dominar programação reativa transforma a forma como você escreve código assincronamente. Três aprendizados essenciais: <strong>(1)</strong> Observables são abstrações poderosas para fluxos de dados que unificam eventos, promises e streams em uma interface consistente; <strong>(2)</strong> RxJS operators como <code>map</code>, <code>filter</code> e <code>switchMap</code> permitem composição elegante e leitura fluida do código, reduzindo callbacks; <strong>(3)</strong> Gerenciamento apropriado de subscrições (com <code>takeUntil</code> ou async pipes) é obrigatório para evitar vazamento de memória em aplicações modernas.</p>
<p>A prática deliberada é fundamental — comece com Observables simples (usando <code>of()</code> e <code>interval()</code>), progressive para eventos do DOM, e então combine múltiplos fluxos. A curva de aprendizado é moderada, mas o retorno em clareza e manutenibilidade do código é exponencial.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://rxjs.dev/" target="_blank" rel="noopener noreferrer">Documentação Oficial RxJS</a></li>
<li><a href="https://rxmarbles.com/" target="_blank" rel="noopener noreferrer">RxJS Marbles - Visualização de Operators</a></li>
<li><a href="https://angular.io/guide/rx-library" target="_blank" rel="noopener noreferrer">Angular - Reactive Programming Guide</a></li>
<li><a href="https://learnrxjs.io/" target="_blank" rel="noopener noreferrer">Learning RxJS - Guia Gratuito Online</a></li>
<li><a href="https://www.reactivemanifesto.org/" target="_blank" rel="noopener noreferrer">Reactive Manifesto</a></li>
</ul>