Entendendo o RxJS: O que são Observables?

Entendendo o RxJS: O que são Observables?

O framework Angular já vem com a biblioteca RxJS instalada por padrão em seus projetos. Se você trabalha ou trabalhou com ele já está familiarizado com essa biblioteca que tem muitos recursos interessantes! Vamos abordar neste artigo as Observables e compreender como elas funcionam com exemplos em código.

O que são?

Podemos definir observables como uma funcionalidade do RxJS que tem mais "poderes" do que as Promises. Elas nos permitem encapsular dados e emitir eles várias vezes e cada emissão pode ter dados diferentes. Ou seja ela não emite um dado apenas uma vez e para por ali, mas pode emitir vários além de poder ser escutada por vários outros métodos que se inscreverem nesse evento! Mas o que tudo isso significa na prática? Vamos entender colocando a mão no código. Mas antes vamos entender um pouco sobre seus operadores e o subscriber.

Subscription

É como se fosse a conexão entre o observer, ou seja, a funcionalidade observable. Quando passamos o subscribe estamos expressando o interesse em ser notificados quando aquela observable emitir algum tipo de dado. É como se fosse uma assinatura que fazemos em um canal do youtube, onde nos inscrevemos e cada vez que um novo vídeo é postado naquele canal, recebemos imediatamente uma notificação. Então concluindo, o subscribe é uma forma de o observador demonstrar interesse quando um evento for iniciado para executar aquele bloco de código.

Com base nisso podemos entender os outros três operadores que temos como acessar dentro de uma observable.

Next(), Complete(), Error()

Quando usamos o objeto Observable temos esses três métodos. Veja o objetivo de cada um deles:

  • next(): É chamado toda vez que é emitido um novo dado, ou seja, quando ocorre uma emissão de dados.

  • complete(): Quando os dados são todos emitidos e não há mais nada para se enviar ou seja a Observable foi finalizada, podemos utilizar o método complete().

  • error(): Podemos utilizar para tratar erros quando não for possível emitir os dados, por algum problema inesperado na requisição.

É importante destacar que todos os observadores que se inscrevem em uma observable serão notificados com essas mensagens.

Entendendo a estrutura de uma Observable

A estrutura é bem simples. Primeiro teremos que instalar a biblioteca, siga esses passos:

Instale o package.json dentro da pasta que definir, com o comando npm init -y. Depois que ele gerar o arquivo, basta rodar o comando npm i rxjs. Vai perceber que terá uma pasta nova, node_modules, dentro terá tudo o que for preciso para executar o projeto tanto no browser quanto no terminal.

Agora podemos escrever este código:

const obs = new Observable(subscriber => {
    subscriber.next('Sua primeria Observable')
})

obs.subscribe(console.log);

Essa é a estrutura básica de uma Observable em JavaScript. Se passar o cursor do mouse em cima dela, vai poder ver os operadores que podemos utilizar que já destacamos anteriormente:

image.png

Um ponto que podemos destacar é o subscribe. Se retirar ele ninguém estará "ouvindo" esse bloco de código, então nunca será executado.

Mas agora vamos entender melhor como as Observables trabalham com várias emissões de dados. Veja o código:

const { Observable } = require('rxjs')

const obs = new Observable(valor => {
    valor.next('Minha')
    valor.next('primeria')
    valor.next('Observable')
})

obs.subscribe(console.log);

setTimeout(() => {
    obs.subscribe(text => console.log(text.toUpperCase()))
}, 2000);

setTimeout(() => {
    obs.subscribe(text => console.log(text[0]))
}, 3500);

Veja que o código tem 3 subscribe. No primeiro exibimos um simples console.log. No segundo transformamos todas as letras para caixa alta. E no terceiro queremos buscar na string a primeira letra de cada elemento. O resultado será esse:

20210209_104214.gif

O que realmente ocorreu? Como temos três interessados no evento, aquela observable terá seu código percorrido 3 vezes. Então no primeiro subscribe ela apenas exibiu as mensagens, depois outro subscribe ele converteu todas as letras para caixa alta e por ultimo buscamos apenas a primeira letra de cada string. Ou seja ele não emitiu todos os valores de uma vez só para todos os interessados, mas foi por demanda. Se tirar o subscribe de qualquer requisição não terá o mesmo resultado.

Vimos que é possível encapsular dados e receber vários mais de uma vez. Mas vamos entender agora como trabalhar com o complete() e o error(). E isso é bem simples:

const { Observable } = require('rxjs')

let num = 142

const observer = new Observable(subscriber => {
    if (num > 15) {
        subscriber.complete()
    } else {
        subscriber.error(`Erro: ${num} Não é maior que 15`)
    }
})


observer.subscribe({
    complete() {
        console.log(`Sucesso: ${num} é maior que 15`)
    },
    error(msg) {
        console.log(msg)
    }
})

Veja que foi definido dentro da própria observable a mensagen de erro. Mas você pode tratar elas utilizando do subscribe conforme o código acima mostra, mas no caso do complete que é um método void, que não retorna nada, diferente do erro onde é possível definir mensagens dentro dele. Então o complete apenas demonstra que aquela observable foi finalizada e que não retornará mais nada. É possível ver isso passando o cursor do mouse sobre cada método:

complete(): image.png

error(): image.png

O resultado se rodar este código tanto para erro como para sucesso exibirá duas mensagens, mas lembre que o complete() não está exibindo mensagem nenhuma mas sim o console.log. Na imagem abaixo os dois resultados de erro e sucesso foram gerados para que tudo ficasse em um print só:

image.png

O que acontece se tentar passar uma mensagem de sucesso dentro do complete()? Terá um undefined conforme vemos na imagem:

image.png

Unsubscribe

Não vamos nos aprofundar tanto agora, mas é bom que você conheça esse operador poderoso do RxJS. Utilizando o unsubscribe() podemos cancelar uma inscrição encerrando assim o ciclo daquela Observable . Para demonstrar isso vamos usar o setInterval() que aciona o evento após um período de tempo e incrementa mais um valor:

const { Observable } = require('rxjs')

const obs = new Observable(obs => {
    let i = 0
    const intervalo = setInterval(() =>
        obs.next(i++)
        , 1500);

    return () => clearInterval(intervalo)
})

const inscrito = obs.subscribe(a => console.log('Valor da Observable ', a))

setTimeout(() => {
    inscrito.unsubscribe();

}, 7000);

O resultado será que a observable não vai ficar sempre gerando valores. Veja o GIF abaixo:

20210209_121350.gif

O unsubscribe evita assim que por algum tipo de descuido uma observable fique gerando várias requisições causando memory leak (vazamento de memória) que ocorre quando memória alocada para uma determinada operação, não é liberada quando já se finalizou aquela execução do código em nossa aplicação. E isso pode causar lentidão e vários outros problemas. Mas existe muitos outros conceitos que podemos entender e aplicar, vamos deixar para abordar esses pontos mais profundo referentes ao unsubscribe em outra oportunidade.

Conclusão

Vimos os operadores e o que de fato a Observable faz e como utilizar ela. Caso queira ler mais sobre o RxJS recomendo que acesse a documentação oficial, basta clicar aqui para acessar. Recomendo que leia outros artigos bem interessantes sobre o essa biblioteca fantástica.

A extensão usada para geral os resultados no terminal do VSCode foi a Code Runner.

Fico por aqui neste post, agradeço por ler. Se tiver alguma dúvida deixe nos comentários ou pode entrar em contato pelo meu LinkedIn . Muito obrigado e até o próximo post. 😉