Assunto vs BehaviorSubject vs ReplaySubject em Angular

122

Tenho procurado entender esses 3:

Assunto , assunto de comportamento e assunto de repetição . Eu gostaria de usá-los e saber quando e por que, quais são os benefícios de usá-los e embora tenha lido a documentação, assistido tutoriais e pesquisado no google, não consegui entender isso.

Então, qual é o seu propósito? Um caso do mundo real seria muito apreciado, pois não é necessário nem codificar.

Eu preferiria uma explicação clara, não apenas "a + b => c você está inscrito ..."

Obrigado

Paul Samsotha
fonte
1
Já existe uma questão com sujeito de comportamento com observável; stackoverflow.com/questions/39494058/… e a documentação sobre o assunto de repetição é clara imo github.com/Reactive-Extensions/RxJS/blob/master/doc/api/…
eko
Há uma apresentação relativamente completa dos assuntos em Rxjs nesta resposta , o que complementa muito bem a resposta do peeksilet. Isso inclui também detalhes importantes sobre o comportamento após a rescisão, por isso é bom dar uma olhada.
user3743222

Respostas:

277

Na verdade, tudo se resume a comportamento e semântica. Com um

  • Subject- um assinante só obterá valores publicados que foram emitidos após a assinatura. Pergunte a si mesmo: é isso que você quer? O assinante precisa saber alguma coisa sobre os valores anteriores? Se não, você pode usar este, caso contrário, escolha um dos outros. Por exemplo, com comunicação componente a componente. Digamos que você tenha um componente que publica eventos para outros componentes em um clique de botão. Você pode usar um serviço com assunto para se comunicar.

  • BehaviorSubject- o último valor é armazenado em cache. Um assinante receberá o valor mais recente na assinatura inicial. A semântica desse assunto é representar um valor que muda com o tempo. Por exemplo, um usuário conectado. O usuário inicial pode ser um usuário anônimo. Mas, uma vez que um usuário efetua login, o novo valor é o estado do usuário autenticado.

    O BehaviorSubjecté inicializado com um valor inicial. Isso às vezes é importante para a preferência de codificação. Digamos, por exemplo, que você o inicialize com um null. Em seguida, em sua assinatura, você precisa fazer uma verificação nula. Talvez OK, ou talvez irritante.

  • ReplaySubject- pode armazenar em cache até um determinado número de emissões. Todos os assinantes obterão todos os valores em cache na assinatura. Quando você precisaria desse comportamento? Sinceramente, não tive necessidade de tal comportamento, exceto no seguinte caso:

    Se você inicializar a ReplaySubjectcom um tamanho de buffer de 1, na verdade ele se comportará como um BehaviorSubject. O último valor é sempre armazenado em cache, por isso age como um valor que muda com o tempo. Com isso, não há necessidade de uma nullverificação como no caso do BehaviorSubjectinicializado com a null. Nesse caso, nenhum valor é emitido ao assinante até a primeira publicação.

Portanto, tudo se resume ao comportamento que você está esperando (quanto a qual usar). Na maioria das vezes, você provavelmente desejará usar um BehaviorSubjectporque o que realmente deseja representar é a semântica do "valor ao longo do tempo". Mas eu pessoalmente não vejo nada de errado com a substituição de ReplaySubjectinicializado com 1.

O que você quer evitar é usar o vanilla Subjectquando o que você realmente precisa é algum comportamento de cache. Por exemplo, você está escrevendo um guarda de roteamento ou uma resolução. Você busca alguns dados naquele guarda e os coloca em um serviço Subject. Então no componente roteado você assina o serviço sujeito para tentar pegar aquele valor que foi emitido na guarda. OOPs. Onde está o valor? Já foi emitido, DUH. Use um assunto de "cache"!

Veja também:

Paul Samsotha
fonte
1
Isso é curto e fácil de entender as diferenças. Quando o valor muda no serviço e os componentes também mudam o valor é mostrado, então BehaviourSubjects ou Replay Subject é a solução.
Saiyaff Farouk
1
Obrigado! ReplaySubjectcom um tamanho de buffer de 1 era exatamente o que eu precisava. Eu tinha um guarda de rota que precisava do valor, mas precisava aguardar a primeira emissão. Então, a BehaviorSubjectnão estava cortando, porque eu não queria um valor inicial ( nulltambém não funcionaria porque eu estava usando para significar um estado)
menehune23
1
@ menehune23 Eu também precisava de ReplaySubject para uma resolveclasse de guarda Angular . Meu serviço de dados pode ser assíncrono ou síncrono (se os dados já tiverem sido recuperados). Se fosse síncrono, o Subject.next () estava sendo disparado antes de a resolvefunção retornar e ser assinada pelo Angular internamente. BehaviourSubject possivelmente funcionaria, mas eu teria que chamar explicitamente complete()e também adicionar nullverificações para o valor inicial. O que funcionou era novo ReplaySubject<DataType>(1) eresolveSubject.asObservable().take(1).map(....)
Drenai
1
Estou usando um ReplaySubject com tamanho de buffer de 1, mas por algum motivo, quando obtenho um Observable com .asObservable()o Observable, envio um valor de nullpara assinantes antes mesmo de chamar next()meu ReplaySubject. Achei que não deveria ter um valor inicial diferente de BehaviorSubject?
Kyle V.
2
Acho que um exemplo muito fácil que você poderia mencionar para o assunto de repetição seria para uma "sala de chat" ou cenário de lobby de jogo onde você deseja que novos participantes vejam as últimas 10 mensagens.
James
14

Um resumo prático dos diferentes tipos observáveis, nomenclatura não intuitiva, eu sei lol .

  • Subject - Um assinante só obterá valores publicados nele - após a assinatura ser feita.
  • BehaviorSubject - Novos assinantes obtêm o último valor publicado OU valor inicial imediatamente após a assinatura.
  • ReplaySubject - Novos assinantes recebem todos os valores publicados anteriormente imediatamente após a assinatura
Ricky Boyce
fonte
Valores publicados 1-n? Portanto, se houvesse 2 valores publicados, um ReplaySubject produziria -1 valores publicados ???
Jason Cheng
@JasonCheng não, ele recupera todos os valores publicados anteriormente na assinatura, atualização de resposta :)
Ricky Boyce
11
  1. Assunto : Na assinatura, ele sempre obtém os dados que são enviados após a assinatura, ou seja, os valores anteriores enviados não são recebidos .
const mySubject = new Rx.Subject();

mySubject.next(1);

const subscription1 = mySubject.subscribe(x => {
  console.log('From subscription 1:', x);
});

mySubject.next(2);

const subscription2 = mySubject.subscribe(x => {
  console.log('From subscription 2:', x);
});

mySubject.next(3);

subscription1.unsubscribe();

mySubject.next(4);

Com este exemplo, aqui está o resultado que será impresso no console:

From subscription 1: 2
From subscription 1: 3
From subscription 2: 3
From subscription 2: 4

Observe como as assinaturas que chegam atrasadas estão perdendo alguns dos dados que foram inseridos no assunto.

  1. Repetir assuntos : pode ajudar mantendo um buffer de valores anteriores que serão emitidos para novas assinaturas.

Aqui está um exemplo de uso para temas de repetição em que buffer of 2 previous valuessão mantidos e emitidos em novas assinaturas:

const mySubject = new Rx.ReplaySubject(2);

mySubject.next(1);
mySubject.next(2);
mySubject.next(3);
mySubject.next(4);

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

Aqui está o que isso nos dá no console:

From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
  1. Assuntos de comportamento : são semelhantes aos assuntos de repetição, mas reemitirão apenas o último valor emitido ou um valor padrão se nenhum valor tiver sido emitido anteriormente:
const mySubject = new Rx.BehaviorSubject('Hey now!');

mySubject.subscribe(x => {
  console.log('From 1st sub:', x);
});

mySubject.next(5);

mySubject.subscribe(x => {
  console.log('From 2nd sub:', x);
});

E o resultado:

From 1st sub: Hey now!
From 1st sub: 5
From 2nd sub: 5

Referência: https://alligator.io/rxjs/subjects/

Varun Sukheja
fonte
4

De: Randall Koutnik livro “Build Reactive Websites with RxJS.” :

Um assunto é um objeto que é um observável turboalimentado. Em sua essência, um Assunto atua como um observável regular, mas cada assinatura está ligada à mesma fonte. Os sujeitos também são observadores e possuem métodos next, error e done para enviar dados a todos os assinantes de uma vez. Como os sujeitos são observadores, eles podem ser passados ​​diretamente para uma chamada de inscrição, e todos os eventos do observável original serão enviados por meio do sujeito para seus assinantes.

Podemos usar o ReplaySubject para rastrear o histórico. Um ReplaySubject registra os últimos n eventos e os envia de volta para cada novo assinante. Por exemplo, no aplicativo de bate-papo. Podemos usá-lo para rastrear o registro do histórico de bate-papo anterior.

Um BehaviorSubject é uma versão simplificada do ReplaySubject . O ReplaySubject armazenou um número arbitrário de eventos, o BehaviorSubject registra apenas o valor do evento mais recente. Sempre que um BehaviorSubject registra uma nova assinatura, ele emite o valor mais recente para o assinante, bem como quaisquer novos valores que são transmitidos. O BehaviorSubject é útil ao lidar com unidades únicas de estado, como opções de configuração.

HS Progr
fonte
1

A resposta mais votada está claramente errada, alegando que:

"Se você inicializar um ReplaySubjectcom um tamanho de buffer de 1, ele realmente se comportará como um BehaviorSubject"


Isso não é totalmente verdade; verifique esta ótima postagem no blog sobre as diferenças entre os dois. Por exemplo, se você assinar um concluído BehaviorSubject, você não receberá o último valor, mas para um ReplaySubject(1)você receberá o último valor.

Esta é uma diferença importante que não deve ser esquecida:

const behavior = new BehaviorSubject(null);
const replay = new ReplaySubject(1);

behavior.skip(1).subscribe(v => console.log('BehaviorSubject:', v));
replay.subscribe(v => console.log('ReplaySubject:', v));

behavior.next(1);
behavior.next(2);
behavior.complete();
behavior.subscribe(v => console.log('Late B subscriber:', v));

replay.next(1);
replay.next(2);
replay.complete();
replay.subscribe(v => console.log('Late R subscriber:', v));

Verifique este exemplo de código aqui, que vem de outra excelente postagem de blog sobre o assunto.

Wilt
fonte
0
     // ***********Subject  concept ***********
    let subject = new Subject<string>();


    subject.next("Eureka");
    subject.subscribe((data) => {
      console.log("Subscriber 1 got data >>>>> "+ data);
    });
    subject.subscribe((data) => {
      console.log("Subscriber 2 got data >>>>> "+ data);
    });

       // ********behaviour subject*********
    // Behavior subjects need a first value
let subject1 = new BehaviorSubject<string>("First value");


subject1.asObservable().subscribe((data) => {
  console.log("First subscriber got data behaviour subject>>>>> "+ data);
});
subject1.next("Second value")
  • Assunto - Um assinante só obterá valores publicados nele - após a assinatura ser feita.
  • BehaviorSubject - Novos assinantes obtêm o último valor publicado OU o valor inicial imediatamente após a assinatura.
Pramod Patil
fonte