RxJS: Primeiros passos e aprendizados de desenvolvimento
Começando por uma definição da própria documentação, o RxJS é uma biblioteca para construção de programas assíncronos ou baseado em eventos, utilizando uma sequência de observables. Não entendeu nada? Vamos tentar separar então alguns pontos dessa frase:
- Programas assíncronos: o exemplo mais simples para esse tipo de programa é quando o seu método, função ou componente no front-end espera alguma resposta de uma API. Não há uma resposta imediata para a sua requisição, ou seja não há uma resposta síncrona mas sim assíncrona ao que você pediu o servidor da API.
- Programas baseados em eventos: se a sua aplicação tem o mínimo de interação com o usuário, provavelmente você tem alguma estrutura de eventos para saber quando o usuário clicou em determinada parte da tela ou quando ele digitou em algum campo, por exemplo. Cada uma dessas ações gera um evento, um alerta de que algo aconteceu. A partir do momento que você tem controle de quando essas ações são realizadas e trabalha no seu código em resposta a elas, você tem um programa baseado em eventos.
- Sequência de observables: Para entender o que é uma sequência de observables, precisamos entender primeiro o que é um observable. Por enquanto, entenda-se como um tipo muito importante para o RxJS, que processa eventos ou valores futuros.
A documentação continua: o RxJS provê operadores, inspirados nos operadores de Array comuns a várias linguagens como map, filter, reduce, every, etc, que permitem lidar com assincronicidade e eventos como se fossem coleções.
Apesar de ser mais utilizado no framework Angular, a biblioteca está no npm e pode ser utilizada em qualquer framework, inclusive substituindo bibliotecas de gerenciamento de estado como o redux.
Por trás do RxJS, nós temos um padrão de projeto bem conhecido. Precisamos pegar carona em alguma das suas ideias para compreender o que foi pensado de estratégia para essa biblioteca.
Sumário
Padrão Observer em RxJS
Um padrão de projeto que pode ser utilizado para lidar com programas baseado em eventos é o Observer. Não à toa, o RxJS baseia a sua estrutura neste padrão. Primeiramente, define-se um objeto denominado Subject, que mantém uma lista de dependentes chamado observers, que são notificados a cada mudança de estado de alguma variável a qual se queira associar um evento.
Para que fique claro, vejamos um diagrama desse padrão:
No diagrama acima, a classe Subject possui uma propriedade com a coleção dos seus observers (observeCollection), um método para registrar novos observers (registerObserver) um método para desassociar observers do seu controle (unregisterObserver) e por fim, o método mais importante, de notificar os observers de alguma atualização (notifyObservers).
Uma interface Observer controla a assinatura do método de atualização em cada instância atrelada ao Subject. Por sua vez, cada observer concreto, possui um método de atualização a seu critério, contando que seja seguido a assinatura da interface.
Basicamente, na maioria das vezes que se deseja enviar uma notificação de estado para algum lugar da sua aplicação, de maneira centralizada, podemos utilizar o padrão de projeto Observer.
Relação do padrão Observer
Ok, mas qual é a relação desse padrão de projeto com o RxJS? Vejamos alguns conceitos essenciais dessa biblioteca, para que naturalmente nós enxerguemos a relação:
- Observable: coleção invocável de valores ou eventos futuros.
- Observer: coleção de callbacks que sabem como ler os valores entregues pelos Observables.
- Subscription (inscrição): representa a execução de um Observable, é primariamente utilizado para cancelar a sua execução.
Conseguimos associar diretamente o conceito de Observer do RxJS com o conceito de Observer do padrão de projeto descrito anteriormente, ambos estão sujeitos a receber valores emitidos por uma entidade, no caso do padrão de projeto, o Subject e no caso do RxJS os Observables com a realização da sua inscrição.
Agora vamos aplicar esses conceitos na prática, com alguns exemplos trazidos da própria documentação da biblioteca.
Exemplo prático da aplicação do padrão na biblioteca
Exemplo 1:
[js]
import { of } from ‘rxjs’;
of(10, 20, 30)
.subscribe(
next => console.log(‘next:’, next),
err => console.log(‘error:’, err),
() => console.log(‘the end’),
);
// Saídas
// next: 10
// next: 20
// next: 30
// Fim
[/js]
Para que possamos explicar qualquer exemplo de RxJS, nós precisamos de alguma fonte de valores ou eventos futuros, um Observable. Para esse exemplo, foi utilizado o operador of, que recebe uma lista de argumentos e retorna um Observable que emitirá cada valor de forma sequencial e completará.
Levando em consideração a saída que esse código emitiu, nós percebemos que cada valor emitido pelo operador of (10, 20 e 30), foi passado para dentro da callback logo abaixo do método subscribe. Mais especificamente, cada valor foi passado somente pela callback com o argumento next.
Considerando esse resultado, vamos quebrar esse código em algumas partes para que possamos entender o que está acontecendo:
import { of } from 'rxjs';
of(10, 20, 30)
Primeiramente, importamos o operador of do pacote rxjs. Em seguida chamamos a função passando três argumentos que serão emitidos em sequência, como explicado anteriormente.
.subscribe(
O método subscribe irá gerar uma Subscription ou seja, é por meio desse método que permitimos a execução da emissão de valores do Observable definido anteriormente. Dentro do subscribe podemos passar 3 argumentos:
next => console.log('next:', next),
err => console.log('error:', err),
() => console.log('the end'),
- O primeiro argumento é uma callback de next, ou seja, essa função irá tratar cada valor emitido pelo Observable, nesse caso, para o operador of, ela será executada para os valores, 10, 20 e 30 (emitidos anteriormente), respectivamente.
- O segundo argumento é uma callback de err, ou seja, essa função será executada caso durante a emissão dos valores do Observable haja algum sinal de erro emitido pelo mesmo.
- O terceiro e último argumento é uma callback de complete. Os Observables podem emitir um sinal de complete, indicando que todos os seus valores ou eventos futuros foram emitidos e a partir de agora nada mais será emitido. No caso do of, esse sinal é emitido junto com a emissão do último valor, no caso do nosso exemplo, o operador emite o valor 30 e o sinal de complete. A callback de complete realiza alguma ação assim que o Observable emite este tipo de sinal.
Ponto de apoio: Marble Diagram
Podemos representar o código do exemplo e a sua execução através de um diagrama chamado marble diagram, ou diagrama de bolinhas:
Esse diagrama é dividido nas seguintes partes:
- O quadrado representa a execução de um operador, com os seus argumentos.
- A seta representa uma passagem de tempo.
- As bolinhas são os valores emitidos ao longo do tempo.
- O traço vertical representa o ponto onde o Observable emitiu o sinal de complete.
Então, assim como vimos, juntamente com o valor 30 emitido pelo operador, ele emite também o sinal de complete, como visto na última bolinha do diagrama.
Conclusão
Dada o nosso exemplo, o mais interessante é pensar que se substituíssemos o operador of por algum Observable que, por exemplo, emitisse cada valor digitado pelo usuário num campo de formulário, poderíamos fazer uma tratativa na função next de limitação de caracteres desse campo, ou uma busca no lado do servidor do termo pesquisado, dentre outras aplicações. Esse é um exemplo do poder do RxJS e seu paradigma reativo.
Neste artigo conseguimos associar um conceito da Engenharia de Software, o padrão Observer, que permitiu a criação do RxJS. Também abordamos algumas peças fundamentais para que você entenda exemplos mais complexos daqui para frente, como a divisão dos argumentos da função subscribe. Dessa maneira você conseguirá ir agregando outras peças a esse entendimento e utilizar melhor esta biblioteca poderosa nos seus projetos.
Gostou do conteúdo? Em nosso podcast técnico, o Entre Chaves, você confere insights como esse para sua carreira de desenvolvedor(a) ou entusiasta do assunto. Te esperamos lá!
Desenvolvimento de Software
Confira outros artigos
Testes end-to-end: sucesso na implementação da automação
Hoje, como você garante a entrega de valor, a confiabilidade e a eficiência do seu projeto? Com o intuito de responder a essa questão crucial, uma das melhores respostas é a automação de testes end-to-end. Muitas pessoas, principalmente nossos clientes, tendem a imaginar que automação de testes é apenas mais um tipo de teste de […]
Desenvolvimento de Software
Mecanismos de Acompanhamento no Desenvolvimento de Software
O uso de mecanismos de acompanhamento é imprescindível no mundo do desenvolvimento de software e pode potencializar a eficiência digital. O acompanhamento efetivo das operações é fundamental para garantir a entrega de soluções digitais de sucesso. Na nossa empresa, utilizamos uma metodologia única, o dti evolve, que incorpora inteligência artificial (IA) para acelerar nosso processo de […]
Desenvolvimento de Software
Eficiência digital com copilot: um caso de uso do GitHub
Em um mundo em constante evolução tecnológica, otimizar o tempo e potencializar a eficiência digital se torna cada vez mais crucial. Portanto, vamos apresentar alguns experimentos que estão sendo implementados com o Git Hub Copilot em busca de maior eficiência digital. Certamente quem nos acompanha sabe que estamos experimentando e introduzindo as melhores ferramentas de […]
Desenvolvimento de Software