BehaviorSubject vs Observable?

690

Estou analisando os padrões de Angx RxJs e não entendo a diferença entre a BehaviorSubjecte an Observable.

Pelo meu entendimento, a BehaviorSubjecté um valor que pode mudar com o tempo (pode ser assinado e os assinantes podem receber resultados atualizados). Esse parece ser exatamente o mesmo objetivo de um Observable.

Quando você usaria um Observablevs a BehaviorSubject? Existem benefícios em usar um BehaviorSubjectover over Observableou vice-versa?

Kevin Mark
fonte

Respostas:

969

BehaviorSubject é um tipo de assunto, um assunto é um tipo especial de observável, para que você possa assinar mensagens como qualquer outro observável. Os recursos exclusivos do BehaviorSubject são:

  • Ele precisa de um valor inicial, pois sempre deve retornar um valor na assinatura, mesmo que não tenha recebido um next()
  • Na assinatura, ele retorna o último valor do assunto. Um observável regular é acionado apenas quando recebe umonnext
  • a qualquer momento, você pode recuperar o último valor do assunto em um código não observável usando o getValue()método

Os recursos exclusivos de um assunto em comparação com um observável são:

  • É um observador, além de ser um observável, para que você também possa enviar valores a um sujeito, além de se inscrever nele.

Além disso, você pode obter uma observação do assunto de comportamento usando o asObservable()método on BehaviorSubject.

Observable é um genérico e BehaviorSubjecttecnicamente é um subtipo de Observable porque BehaviorSubject é um observável com qualidades específicas.

Exemplo com BehaviorSubject :

// Behavior Subject

// a is an initial value. if there is a subscription 
// after this, it would get "a" value immediately
let bSubject = new BehaviorSubject("a"); 

bSubject.next("b");

bSubject.subscribe(value => {
  console.log("Subscription got", value); // Subscription got b, 
                                          // ^ This would not happen 
                                          // for a generic observable 
                                          // or generic subject by default
});

bSubject.next("c"); // Subscription got c
bSubject.next("d"); // Subscription got d

Exemplo 2 com assunto regular:

// Regular Subject

let subject = new Subject(); 

subject.next("b");

subject.subscribe(value => {
  console.log("Subscription got", value); // Subscription wont get 
                                          // anything at this point
});

subject.next("c"); // Subscription got c
subject.next("d"); // Subscription got d

Um observável pode ser criado a partir de ambos Subjecte BehaviorSubjectusando subject.asObservable().

A única diferença é que você não pode enviar valores para um next()método observável usando .

Nos serviços Angular, eu usaria BehaviorSubjectpara um serviço de dados como um serviço angular, muitas vezes inicializado antes do componente e do comportamento garantirem que o componente que consome o serviço receba os últimos dados atualizados, mesmo que não haja novas atualizações desde a assinatura do componente para esses dados.

Shantanu Bhadoria
fonte
7
Estou um pouco confuso com o exemplo 2 da matéria regular. Por que a assinatura não receberá nada na segunda linha em que você envia valores para o assunto usando subject.next ("b")?
jmod999
25
@ jmod999 O segundo exemplo é um assunto comum que recebe um valor imediatamente antes da assinatura da assinatura. Em assuntos regulares, a assinatura é acionada apenas para valores recebidos depois que a assinatura é chamada. Como a é recebida logo antes da assinatura, ela não é enviada para a assinatura.
Shantanu Bhadoria 19/04
Uma observação sobre essa solução fantástica, se você a usar em uma função e devolvê-la, retorne um observável. Eu tive alguns problemas com o retorno de um assunto, e confunde os outros desenvolvedores que só sabe o que são observáveis
sam
8
Eu tive uma entrevista no Angular 4 na quarta-feira. Como ainda estou aprendendo a nova plataforma, ele me enganou perguntando algo como "O que vai acontecer se eu assinar um observável que está em um módulo que ainda não foi carregado preguiçosamente?" Eu não tinha certeza, mas ele me disse que a resposta era usar um BSubject - EXATAMENTE como o Sr. Bhadoria explicou isso acima. A resposta foi usar um BSubject porque ele sempre retorna o valor mais recente (pelo menos é assim que me lembro do comentário final do entrevistador).
bob.mazzo
1
@ bob.mazzo Por que preciso usar um BSubject para esse caso? - Se eu assinar o Observador, não receberei nada porque o observador não foi inicializado, portanto não pode enviar dados aos observadores e, se eu usar um BSubject, também não receberei nada pelo mesmo motivo. Nos dois casos, o assinante não receberá nada porque está dentro de um módulo que não foi inicializado. Estou certo?
Rafael Reyes
183

Observável: Resultado diferente para cada Observador

Uma diferença muito, muito importante. Como Observable é apenas uma função, ele não possui nenhum estado; portanto, para cada novo Observador, ele executa o código de criação observável repetidamente. Isto resulta em:

O código é executado para cada observador. Se for uma chamada HTTP, ela será chamada para cada observador

Isso causa grandes erros e ineficiências

BehaviorSubject (ou Assunto) armazena detalhes do observador, executa o código apenas uma vez e fornece o resultado a todos os observadores.

Ex:

JSBin: http://jsbin.com/qowulet/edit?js,console

// --- Observable ---
let randomNumGenerator1 = Rx.Observable.create(observer => {
   observer.next(Math.random());
});

let observer1 = randomNumGenerator1
      .subscribe(num => console.log('observer 1: '+ num));

let observer2 = randomNumGenerator1
      .subscribe(num => console.log('observer 2: '+ num));


// ------ BehaviorSubject/ Subject

let randomNumGenerator2 = new Rx.BehaviorSubject(0);
randomNumGenerator2.next(Math.random());

let observer1Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 1: '+ num));
      
let observer2Subject = randomNumGenerator2
      .subscribe(num=> console.log('observer subject 2: '+ num));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.3/Rx.min.js"></script>

Resultado :

"observer 1: 0.7184075243594013"
"observer 2: 0.41271850211336103"
"observer subject 1: 0.8034263165479893"
"observer subject 2: 0.8034263165479893"

Observe como o uso de Observable.createresultados diferentes criados para cada observador, mas BehaviorSubjectdeu o mesmo resultado para todos os observadores. Isso é importante.


Outras diferenças resumidas.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃         Observable                  ┃     BehaviorSubject/Subject         ┃      
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ 
┃ Is just a function, no state        ┃ Has state. Stores data in memory    ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Code run for each observer          ┃ Same code run                       ┃
┃                                     ┃ only once for all observers         ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Creates only Observable             ┃Can create and also listen Observable┃
┃ ( data producer alone )             ┃ ( data producer and consumer )      ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ Usage: Simple Observable with only  ┃ Usage:                              ┃
┃ one Obeserver.                      ┃ * Store data and modify frequently  ┃
┃                                     ┃ * Multiple observers listen to data ┃
┃                                     ┃ * Proxy between Observable  and     ┃
┃                                     ┃   Observer                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Vamshi
fonte
3
qualquer um que KnockoutJS's ko.observable()venha imediatamente verá mais paralelos em Rx.BehaviorSubjectcomparação com #Rx.Observable
Simon_Weaver
@Skeptor Observable: o método de inscrição sempre acionará o método onNext associado ao observador e trará o valor de retorno. BehaviourSubject / Subject: sempre retornará o valor mais recente no fluxo. aqui, o método subcribe com o assunto não será acionado no método NextNext do Observer até encontrar o valor mais recente no fluxo.
Mohan Ram
62

Observável e sujeito são os meios observáveis ​​que um observador pode rastreá-los. mas ambos têm algumas características únicas. Além disso, existem 3 tipos de sujeitos no total, cada um deles novamente com características únicas. vamos tentar entender cada um deles.

Você pode encontrar o exemplo prático aqui no stackblitz . (Você precisa verificar o console para ver a saída real)

insira a descrição da imagem aqui

Observables

Eles são frios: o código é executado quando eles têm pelo menos um único observador.

Cria cópia de dados: Observable cria cópia de dados para cada observador.

Unidirecional: o observador não pode atribuir valor a observável (origem / mestre).

Subject

Eles são quentes: o código é executado e o valor é transmitido, mesmo que não haja observador.

Compartilha dados: os mesmos dados são compartilhados entre todos os observadores.

bidirecional: o observador pode atribuir valor a observável (origem / mestre).

Se você estiver usando o assunto, você perderá todos os valores transmitidos antes da criação do observador. Então aqui vem o Assunto do Replay

ReplaySubject

Eles são quentes: o código é executado e o valor é transmitido, mesmo que não haja observador.

Compartilha dados: os mesmos dados são compartilhados entre todos os observadores.

bidirecional: o observador pode atribuir valor a observável (origem / mestre). mais

Reproduzir o fluxo de mensagens: não importa quando você assina o assunto da reprodução, receberá todas as mensagens transmitidas.

No assunto e no assunto da reprodução, você não pode definir o valor inicial como observável. Então aqui vem Assunto Comportamental

BehaviorSubject

Eles são quentes: o código é executado e o valor é transmitido, mesmo que não haja observador.

Compartilha dados: os mesmos dados são compartilhados entre todos os observadores.

bidirecional: o observador pode atribuir valor a observável (origem / mestre). mais

Reproduzir o fluxo de mensagens: não importa quando você assina o assunto da reprodução, receberá todas as mensagens transmitidas.

Você pode definir o valor inicial: Você pode inicializar o observável com o valor padrão.

Kedar9444
fonte
3
Vale a pena mencionar que a ReplaySubjectpossui um histórico e pode transmitir / emitir uma sequência de valores (antigos). Somente quando o buffer é definido como 1, ele se comporta de maneira semelhante a a BehaviorSubject.
Wilt
28

O objeto Observable representa uma coleção baseada em envio.

As interfaces Observer e Observable fornecem um mecanismo generalizado para notificação por push, também conhecido como padrão de design do observador. O objeto Observable representa o objeto que envia notificações (o provedor); o objeto Observer representa a classe que os recebe (o observador).

A classe Subject herda Observable e Observer, no sentido de que é tanto um observador quanto um observável. Você pode usar um assunto para assinar todos os observadores e, em seguida, assinar o assunto em uma fonte de dados de back-end

var subject = new Rx.Subject();

var subscription = subject.subscribe(
    function (x) { console.log('onNext: ' + x); },
    function (e) { console.log('onError: ' + e.message); },
    function () { console.log('onCompleted'); });

subject.onNext(1);
// => onNext: 1

subject.onNext(2);
// => onNext: 2

subject.onCompleted();
// => onCompleted

subscription.dispose();

Mais em https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

Md Ayub Ali Sarker
fonte
qual é a diferença entre subscription.dispose () e subscription.unsubscribe ()?
choopage - Jek Bao
4
@choopage nenhuma diferença. o último é o novo caminho
Royi Namir
Se a inscrição for cancelada antes que o assunto seja descartado, a assinatura se tornará um lixo, pois se assina com um valor nulo.
Sophie Zhang
20

Uma coisa que não vejo nos exemplos é que, quando você lança BehaviorSubject para Observable via asObservable, ele herda o comportamento de retornar o último valor na assinatura.

É um pouco complicado, pois as bibliotecas costumam expor os campos como observáveis ​​(por exemplo, parâmetros no ActivatedRoute no Angular2), mas podem usar Subject ou BehaviorSubject nos bastidores. O que eles usam afetaria o comportamento da inscrição.

Veja aqui http://jsbin.com/ziquxapubo/edit?html,js,console

let A = new Rx.Subject();
let B = new Rx.BehaviorSubject(0);

A.next(1);
B.next(1);

A.asObservable().subscribe(n => console.log('A', n));
B.asObservable().subscribe(n => console.log('B', n));

A.next(2);
B.next(2);
Lukasz Marek Sielski
fonte
11

Um observável permite que você se inscreva apenas, enquanto um assunto permite que você publique e se inscreva.

Portanto, um assunto permite que seus serviços sejam usados ​​tanto como publicador quanto como assinante.

A partir de agora, eu não sou tão bom Observableassim, vou compartilhar apenas um exemplo de Subject.

Vamos entender melhor com um exemplo de CLI Angular . Execute os comandos abaixo:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Substitua o conteúdo de app.component.htmlpor:

<div *ngIf="message">
  {{message}}
</div>

<app-home>
</app-home>

Execute o comando ng g c components/homepara gerar o componente inicial. Substitua o conteúdo de home.component.htmlpor:

<input type="text" placeholder="Enter message" #message>
<button type="button" (click)="setMessage(message)" >Send message</button>

#messageé a variável local aqui. Adicione uma propriedade message: string; à app.component.tsclasse de.

Execute este comando ng g s service/message. Isso irá gerar um serviço em src\app\service\message.service.ts. Forneça este serviço ao aplicativo .

Importar Subjectpara MessageService. Adicione um assunto também. O código final deve ficar assim:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MessageService {

  public message = new Subject<string>();

  setMessage(value: string) {
    this.message.next(value); //it is publishing this value to all the subscribers that have already subscribed to this message
  }
}

Agora, injete esse serviço home.component.tse passe uma instância para o construtor. Faça isso app.component.tstambém. Use esta instância de serviço para passar o valor de #messagepara a função de serviço setMessage:

import { Component } from '@angular/core';
import { MessageService } from '../../service/message.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent {

  constructor(public messageService:MessageService) { }

  setMessage(event) {
    console.log(event.value);
    this.messageService.setMessage(event.value);
  }
}

Por dentro app.component.ts, inscreva-se e cancele-a (para evitar vazamentos de memória) no Subject:

import { Component, OnDestroy } from '@angular/core';
import { MessageService } from './service/message.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  message: string;
  subscription: Subscription;

  constructor(public messageService: MessageService) { }

  ngOnInit() {
    this.subscription = this.messageService.message.subscribe(
      (message) => {
        this.message = message;
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

É isso aí.

Agora, qualquer valor inserido dentro #messagede home.component.htmldeve ser impresso em {{message}}dentroapp.component.html

xameeramir
fonte
Por que a imagem gigante? Se não estiver diretamente relacionado à sua resposta, parece uma votação.
Ruffin #
@ ruffin Esta é apenas uma resposta média com número médio de votos, veja o meu perfil. Não é definitivamente voto: D
xameeramir
1
Dei a você um voto anterior, mas você se esquivou da questão de por que a imagem está lá. Não está diretamente relacionado à sua resposta. Não importa se você tem muitos representantes ou não - se a imagem não é direta e especificamente elucidativa, solicito que você a remova . / shrug
ruffin
1
@ruffin Se for contra o consentimento da comunidade, não deve estar lá com certeza!
Xameeramir 6/10/19
4

app.component.ts

behaviourService.setName("behaviour");

behaviour.service.ts

private name = new BehaviorSubject("");
getName = this.name.asObservable();`

constructor() {}

setName(data) {
    this.name.next(data);
}

custom.component.ts

behaviourService.subscribe(response=>{
    console.log(response);    //output: behaviour
});
Chandru Dev
fonte
1

BehaviorSubject vs Observable : o RxJS possui observadores e observáveis, o Rxjs oferece várias classes para usar com fluxos de dados, e uma delas é o BehaviorSubject.

Observáveis : Observáveis ​​são coleções preguiçosas de vários valores ao longo do tempo.

BehaviorSubject : um assunto que requer um valor inicial e emite seu valor atual para novos assinantes.

 // RxJS v6+
import { BehaviorSubject } from 'rxjs';

const subject = new BehaviorSubject(123);

//two new subscribers will get initial value => output: 123, 123
subject.subscribe(console.log);
subject.subscribe(console.log);

//two subscribers will get new value => output: 456, 456
subject.next(456);

//new subscriber will get latest value (456) => output: 456
subject.subscribe(console.log);

//all three subscribers will get new value => output: 789, 789, 789
subject.next(789);

// output: 123, 123, 456, 456, 456, 789, 789, 789
Instantâneo
fonte
1

Pense no Observables como um cano com água corrente, às vezes a água flui e às vezes não. Em alguns casos, você pode realmente precisar de um cano que sempre tenha água, criando um cano especial que sempre contém uma água, por menor que seja, vamos chamar esse cano especial de BehaviorSubject , se você estiver como fornecedor de água na sua comunidade, você pode dormir em paz à noite, sabendo que o seu tubo recém-instalado simplesmente funciona.

Em termos técnicos: você pode encontrar casos em que um Observable deve sempre ter valor nele; talvez você queira capturar o valor de um texto de entrada ao longo do tempo; em seguida, você pode criar uma instância de BehaviorSubject para garantir esse tipo de comportamento, digamos:


const firstNameChanges = new BehaviorSubject("<empty>");

// pass value changes.
firstNameChanges.next("Jon");
firstNameChanges.next("Arya");

Você pode usar o "valor" para provar alterações ao longo do tempo.


firstNameChanges.value;

Isso é útil quando você combina o Observables mais tarde, analisando o tipo de seu fluxo como BehaviorSubject, você pode garantir que o fluxo pelo menos seja acionado ou sinalize apenas uma vez pelo menos .

Ronnel Reposo
fonte