Skip to main content

Command Palette

Search for a command to run...

Entendendo o RxJS: Subject vs Observable

Quais as diferenças entre uma Observable e um Subject?

Updated
Entendendo o RxJS: Subject vs Observable
R

Hi, I'm Rafael, but you can call me Rafa! 😄 I'm a software engineer passionate about everything that involves technology, with more than 5 years of experience in the market. Throughout my career, I worked in banking institutions, developing innovative solutions and contributing to the digital transformation of the financial sector.

With a strong interest in software architecture and testing, I am constantly looking to specialize and improve my skills in this area.

Here, you will find tips, tutorials and discussions on the most varied topics related to architecture, good practices and software testing, always with the aim of sharing knowledge and learning from other developers. I value the exchange of experiences and I am always willing to listen and learn from my professional colleagues.

Feel free to get in contact, leave your comments and share your own experiences and knowledge. Together, we can build a stronger, more collaborative community in the software development world.

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: https://rxjs.dev/guide/subject

Referência:

Pattern Observer: https://refactoring.guru/pt-br/design-patterns/observer

https://rafaeldeveloper.hashnode.dev/entendendo-o-padrao-observer-com-rxjs

https://rafaeldeveloper.hashnode.dev/entendendo-o-rxjs-o-que-sao-observables

https://javascript.plainenglish.io/eli5-observables-vs-subjects-vs-behavior-subjects-f2494f14813d