Entendendo o RxJS: Subject vs Observable

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:

image.png

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:

1.png

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:

20211208_183102.gif

Voltando agora para o código, veja o resultado que temos quando executamos com o Subject:

image.png

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.

2.png

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 👇 :

image.png

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..

rafaeldeveloper.hashnode.dev/entendendo-o-r..

javascript.plainenglish.io/eli5-observables..