DEV Community

Felipe Carvalho
Felipe Carvalho

Posted on

Observables: como funcionam?

Introdução

Observable é uma funcionalidade da biblioteca rxjs, que é utilizada internamente pelo framework e já é instalada quando você cria uma nova aplicação Angular. Com Observables, conseguimos lidar com transferência de dados assíncrona. Muitas vezes, seu uso é semelhante ao de Promises do Javascript, porém, podendo ser uma fonte de transferência de dados contínua, ou seja, o Observable poderá emitir dados várias vezes em momentos distintos de sua existência.


Uso

Seu uso consiste em basicamente se inscrever (subscribe) à um Observable, informando o que deverá ser feito com o dado que irá receber. Essa inscrição poderá escutar 3 interações: sucesso, erro e completo (encerrado). Podemos informar no próprio subscribe, via parâmetro, as funções que devem ser executadas quando alguma dessas interações ocorrerem, sendo somente função de sucesso obrigatória.

É importante ter em mente que ao emitir um erro, o Observable sempre encerrará sua execução, podendo passar algum dado. Ao completar, ele apenas encerra a execução, sem passar nenhum dado para os inscritos, apenas os informa que ele completou seu ciclo.

Podemos nos inscrever ao mesmo Observable mais de uma vez e em vários pontos da aplicação, tornado-os uma excelente alternativa para comunicação entre componentes, o que recomendo ser feito com usando Subject ou BehaviorSubject, que derivam de Observables e têm rápida implementação. Deixarei para abordá-los em outro post.


Resumo

Resumidamente, com um Observable podemos:
• Receber dados várias vezes e em vários momentos
• Nos inscrever para receber dados de um mesmo Observable em vários pontos da aplicação
• Executar alguma operação quando o dado for recebido com sucesso
• Executar alguma operação quando emitir erro
• Executar alguma operação quando completar


Exemplo

Não é tão comum criar Observables “puros”, mas acredito que seja a melhor maneira para entender o seu funcionamento. Detalharei um pouco cada trecho do código e logo após disponibilizarei um exemplo funcional no stackblitz.

No Observable, defini o observador responsável por emitir algum dado para todos os inscritos, observadores, através do método next().

  novoObservable(): Observable<string> {
    return new Observable<string>(observador => {
      setTimeout(() => {
        observador.next("Primeiro timeout");
      }, 2000);

      setTimeout(() => {
        observador.next("Segundo timeout");
      }, 3000);

      setTimeout(() => {
        observador.next("Terceiro timeout");
      }, 5000);

      setTimeout(() => {
        observador.next("Quarto timeout");
      }, 4000);
    });
  }
Enter fullscreen mode Exit fullscreen mode

Para receber os dados do Observable é necessário se inscrever nele através do método subscribe(). No ngOnInit() do meu componente, me inscrevi no Observable criado anteriormente, passando como parâmetro as funções de sucesso, erro e encerramento. Basicamente, irei adicionar a uma lista os resultados emitidos para exibi-la na tela. Chamamos isso de uma Subscription.

  ngOnInit() {
    const observable = this.novoObservable();

    this.inscricaoObservable = observable.subscribe(
      valor => {
        this.valoresRecebidos.push(valor);
      },
      erro => {
        this.valoresRecebidos.push(erro);
      },
      () => {
        this.valoresRecebidos.push("O observable foi encerrado!");
      });
  }
Enter fullscreen mode Exit fullscreen mode

Além do método next(), temos o error(), ao qual, no exemplo abaixo, passo uma mensagem de erro que, da mesma forma que passo dados através do next(), o Observable a emitirá à todos observadores.

Disparei um erro no Segundo timeout, o que irá interromper o Observable antes de emitir o Terceiro e Quarto timeouts. Isso acionará a segunda função que passamos por parâmetro na nossa Subscription, adicionando o resultado "Erro no observable!" à nossa lista de valores recebidos.

  novoObservable(): Observable<string> {
    return new Observable<string>(observador => {
      setTimeout(() => {
        observador.next("Primeiro timeout");
      }, 2000);

      setTimeout(() => {
        observador.next("Segundo timeout");
        observador.error("Erro no observable!");
      }, 3000);

      setTimeout(() => {
        observador.next("Terceiro timeout");
      }, 5000);

      setTimeout(() => {
        observador.next("Quarto timeout");
      }, 4000);
    });
  }
Enter fullscreen mode Exit fullscreen mode

Por fim, o observador também disponibiliza o método complete(), que aciona a terceira função que passamos por parâmetro, encerrando o Observable no mesmo instante, porém, sem passar nenhuma informação. O valor a ser inserido na lista de valores foi definido na própria função.

  novoObservable(): Observable<string> {
    return new Observable<string>(observador => {
      setTimeout(() => {
        observador.next("Primeiro timeout");
      }, 2000);

      setTimeout(() => {
        observador.next("Segundo timeout");
        observador.complete();
      }, 3000);

      setTimeout(() => {
        observador.next("Terceiro timeout");
      }, 5000);

      setTimeout(() => {
        observador.next("Quarto timeout");
      }, 4000);
    });
  }
Enter fullscreen mode Exit fullscreen mode

Veja funcionando:

Para testar o exemplo, talvez seja necessário atualizar o navegador que ele disponibiliza. Remova os os trechos comentados (Ctrl K, U) da criação do Observable e repare que quando um error() ou complete() são acionados, o Observable é interrompido e não emite todos os dados. Experimente também remover as funções que passamos por parâmetro para o Observable, deixando apenas a de sucesso (primeira).

Caso não consiga ver o embedded, clique aqui.

Cancelar inscrição!

Repare que também criei a variável chamada Subscription no exemplo anterior. Se desinscrever dos Observables é uma boa prática que deve não deve ser esquecida!
Mais detalhes neste post: Observables: Cancelar inscrição (unsubscribe) é importante!

Top comments (4)

Collapse
 
jhonatan_godoi_b1ae8436b9 profile image
Jhonatan Godoi

Muito bom o conteúdo. Porém agora ele precisa ser atualizado. O seguinte exemplo, que foi mostrado acima, está em desuso:

this.inscricaoObservable = observable.subscribe(
valor => {
this.valoresRecebidos.push(valor);
},
erro => {
this.valoresRecebidos.push(erro);
},
() => {
this.valoresRecebidos.push("O observable foi encerrado!");
});

Collapse
 
cgodevs profile image
Caroline G.O.

Material excelente, ajudou muito

Collapse
 
nadineschneider profile image
Nadine Schneider

Parabéns pelo ótimo conteúdo! Objetivo e fácil de entender e aplicar na prática!

Collapse
 
ronaldohoch profile image
Ronaldo Hoch

Bah, muito bom cara!
Simples e extremamente útil!