Entendendo o RxJS: Subject vs Observable
Quais as diferenças entre uma Observable e um Subject?
Muitos se confundem ao determinar o que é o Subject e suas diferenças para a Observable. Já foquei sobre as Observables alguns meses atrás. Se não teve a oportunidade de ler vou deixar o link para esse artigo ao final deste. Vamos agora entender melhor sobre ele!
O que é um Subject?
A própria documentação do RxJS define o Subject como um tipo especial de Observable. O Subject segue a mesma lógica do padrão Observer, ou seja, a cada evento que ocorre na aplicação, ele notifica aqueles que se inscreveram (subscribe) no evento. Então se o evento for um clique do mouse em um button, obviamente ele notifica quem se inscreveu naquele evento e transmite os dados, mas com um detalhe importante que difere da observable "comum" e para que isso fique claro, precisamos de uma analogia.
Analogia do Inscrito
A partir daqui faço um pedido, leia com calma o que vamos abordar, para entender o assunto e sanar suas dúvidas. Se for preciso leia novamente e coloque em prática os exemplos. Vamos para a analogia:
Imagine que você está navegando no youtube e encontre um canal de seu interesse. Se desejar receber notificações do canal, precisa clicar no botão de inscrição. O subscribing conecta você, o consumidor, aos dados desse canal. O canal do youtube é como uma observable que contém os dados e ao se inscrever, você (o observador) será notificado quando um novo vídeo for produzido. Caso se esqueça de realizar a inscrição, não receberá as atualizações mais recentes e vai perder qualquer novo vídeo lançado naquele canal.
Se leu com atenção para a documentação vai reparar que existe a palavra multicast ao se referir a um Subject. O que isso significa dentro do contexto do RxJS?
Se pesquisar agora no google sobre multicast, vai encontrar a seguinte definição:
" Multicasting é um método ou técnica de transmissão de um pacote de dados para múltiplos destinos ao mesmo tempo. "
Simplificando para a analogia do canal do youtube, o comportamento do Subject é multicast pois vários observadores estão ouvindo as mudanças (atualizações). Quando se inscreve, você é apenas um entre muitos assinantes/observadores. Se cancelar a assinatura, a observable (o Subject é um tipo especial de Observable) continuará a produzir valores até que todos cancelem a assinatura. Mas calma vamos nos aprofundar mais nisso em breve. Mas antes vamos analisar melhor como funciona a Observable unicast.
O comportamento Unicast
A Observable "comum" é unicast, isso significa que para cada dado emitido dela, é criado uma instância independente para aquele inscrito. Ainda não ficou claro esse comportamento Unicast? Veja mais uma analogia:
Se você assistir a um filme no Netflix em sua casa, o fluxo do filme será criado para você e o filme estará totalmente disponível do início ao fim quando você pressionar o play (assinar). No caso tente imaginar que a ação apertar o play é equivalente a se inscrever (subscriber).
Isso seria um comportamento unicast porque o fluxo do filme (também conhecido como Observable) é criado apenas para você que é o único observador (unicast) para este fluxo do filme e tem controle total de quando ele começa ou para. Se qualquer outra pessoa no planeta pressiona play, um novo fluxo observable é produzido e eles podem ver o filme completo também, ou seja, um novo fluxo independente é criado para eles e o novo observador tem controle total para iniciar / interromper o fluxo dessa instancia do vídeo (Observable emitindo dados) assinando ou cancelando a assinatura. Mas a instancia do segundo inscrito não é a mesma que a sua!
Mas como isso ocorre na prática? Vamos entender com um código bem simples.
Comportamento da Observable "comum" em código!
function getObservable() {
return new Observable(subscriber => {
setTimeout(() => {
console.log(' Observable...')
subscriber.next(Math.random())
subscriber.complete()
}, 1000)
})
}
const observer = getObservable()
observer.subscribe(console.log)
observer.subscribe(console.log)
A prova está nesse simples código acima. Repare que dentro da Observable, passamos um console.log, que servirá para mostrar quantas vezes a função foi chamada para cada subscriber e um Math.random que irá cuidar de gerar números aleatórios para o nosso teste. O resultado que temos é esse:
Quando executamos o código acima vimos o comportamento unicast da Observable. O primeiro subscriber na linha 14 recebeu um valor diferente do que está na linha 15. Então quando o segundo observador assinou interessado naquele evento na linha 15, a Observable criou uma nova instancia e percorreu todo seu escopo incluindo o Math.random que nos gerou um novo número aleatório e assim emitiu um valor diferente para aquele inscrito! Ela não transmitiu os mesmos valores, porque a Observable é unicast, ela cria instancias dela para cada subscriber ! Segue uma simples ilustração sobre esse comportamento:
Espero que este comportamento Unicast da Observable tenha ficado bem claro.
Mas agora vamos voltar ao nosso foco que é o Subject.
Comportamento Multicast do Subject
Vamos fazer mais uma analogia para tentar deixar tudo bem claro:
Imagine que você está no cinema, você tem controle pra pausar o filme e sair para ir ao banheiro? Não! Você não controla quando o filme começa, porque ele começa em um horário de exibição pré-definido. E se você se atrasar, perderá o início do filme. Qual é o ponto? As salas de cinema têm vários observadores (inscritos) e um filme para assistir (stream/evento). É esse o comportamento do Subject transmitir a mesma informação para vários inscritos. Você só pode reagir às coisas que estava prestando atenção!
Veja agora em código como ele emite dados para seus observadores.
function getSubjectRxJS() {
const subject = new Subject()
setTimeout(() => {
console.log(' Subject...')
subject.next(Math.random())
subject.complete()
}, 1000)
return subject
}
const subject = getSubjectRxJS()
subject.subscribe(console.log)
subject.subscribe(console.log)
A lógica permanece a mesma o que apenas muda é sintaxe na chamada do Subject, pois ele não recebe parâmetros no seu construtor diferente da Observable que recebe, isso pode ser comprovado passando o mouse em cima das classes. Veja no GIF:
Voltando agora para o código, veja o resultado que temos quando executamos com o Subject:
O resultado é que para os dois interessados no evento na linha 46 e 47 tivemos o mesmo valor! Esse é o comportamento multicast, ou seja um único tipo de dado para vários observadores. Na prática isso demonstra a clara diferença entre eles. Por isso é importante entender bem as vantagens de usar cada um dentro do seu Software.
E sim podemos manipular ou realizar o tratamento dos dados que recebemos do Subject da mesma maneira que podemos realizar com observables unicast. Veja um exemplo bem simples:
function getSubjectRxJS() {
const subject = new Subject()
setTimeout(() => {
console.log('#2 Subject...')
subject.next(Math.random())
subject.complete()
}, 1000)
return subject
}
const sub = getSubjectRxJS()
sub.subscribe(console.log)
sub.subscribe(x => {
if (x > 0.7) {
console.log(" É maior que 0.7 ");
} else{
console.log("Não é maior que 0.7");
}
})
Resultado depois de executar o código duas vezes 👇 :
Além do Subject temos outros objetos que podemos usar ao nosso favor que são:
- BehaviorSubject
- ReplaySubject
- AsyncSubject
Mas para que este artigo não fique longo, vamos conversar sobre eles em outro momento.
Antes de encerrar é importante destacar que do ponto de vista do Observador ele nunca sabe se a execução vem de um Observable unicast simples ou de um Subjectque é multicast. Isso está escrito na documentação do RxJS.
Conclusão
O RxJS é uma poderosa biblioteca que tem como base o Pattern Observer. E com ela podemos resolver problemas na vida real. Mas também devemos saber quando ou não usar esses métodos e objetos. Por isso sempre veja o contexto como um todo e analise quando é necessário tal implementação e quando não é. O intuito neste artigo é explicar o Subject e suas principais diferenças para a Observable.
Espero que este artigo tenha deixado claro as diferenças. Fico por aqui neste post, agradeço por ler. Se tiver alguma dúvida ou crítica construtiva deixe nos comentários. Muito obrigado e até o próximo post. 😉
Artigos comentados no inicio:
Documentação RxJS: rxjs.dev/guide/subject
Referência:
Pattern Observer: refactoring.guru/pt-br/design-patterns/obse..
rafaeldeveloper.hashnode.dev/entendendo-o-p..