BehaviorSubject vs Observable?

690

Estoy investigando los patrones de Angular RxJs y no entiendo la diferencia entre a BehaviorSubjecty an Observable.

Según tengo entendido, a BehaviorSubjectes un valor que puede cambiar con el tiempo (puede suscribirse y los suscriptores pueden recibir resultados actualizados). Este parece ser exactamente el mismo propósito de un Observable.

¿Cuándo Observableusarías un vs a BehaviorSubject? ¿Existen beneficios al usar un BehaviorSubjectover an Observableo viceversa?

Kevin Mark
fuente

Respuestas:

969

BehaviorSubject es un tipo de asunto, un asunto es un tipo especial de observable para que pueda suscribirse a mensajes como cualquier otro observable. Las características únicas de BehaviorSubject son:

  • Necesita un valor inicial, ya que siempre debe devolver un valor en la suscripción, incluso si no ha recibido un next()
  • Tras la suscripción, devuelve el último valor del tema. Un observable regular solo se dispara cuando recibe unonnext
  • en cualquier momento, puede recuperar el último valor del sujeto en un código no observable utilizando el getValue()método

Las características únicas de un sujeto en comparación con un observable son:

  • Es un observador además de ser un observable, por lo que también puede enviar valores a un sujeto además de suscribirse a él.

Además, puede obtener un sujeto observable del comportamiento utilizando el asObservable()método activado BehaviorSubject.

Observable es un genérico, y BehaviorSubjecttécnicamente es un subtipo de Observable porque BehaviorSubject es un observable con cualidades específicas.

Ejemplo con 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

Ejemplo 2 con sujeto 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

Se puede crear un observable desde ambos Subjecty BehaviorSubjectusando subject.asObservable().

La única diferencia es que no puede enviar valores a un next()método observable .

En los servicios angulares, lo usaría BehaviorSubjectpara un servicio de datos, ya que un servicio angular a menudo se inicializa antes de que el componente y el sujeto de comportamiento garanticen que el componente que consume el servicio reciba los últimos datos actualizados incluso si no hay nuevas actualizaciones desde la suscripción del componente a estos datos.

Shantanu Bhadoria
fuente
77
Estoy un poco confundido con el ejemplo 2 del tema regular. ¿Por qué la suscripción no obtendrá nada, incluso en la segunda línea que envía valores al sujeto usando subject.next ("b")?
jmod999
25
@ jmod999 El segundo ejemplo es un tema regular que recibe un valor justo antes de que se llame a la suscripción. En temas regulares, la suscripción solo se activa para valores recibidos después de que se llama a la suscripción. Como a se recibe justo antes de la suscripción, no se envía a la suscripción.
Shantanu Bhadoria
Una nota sobre esa fantástica solución, si la usa en una función y la devuelve, devuelve un observable. Tuve algunos problemas para devolver un tema, y ​​confunde a los otros desarrolladores que solo saben lo que son Observables
Sam
8
Tuve una entrevista de Angular 4 el miércoles. Como todavía estoy aprendiendo la nueva plataforma, me hizo tropezar preguntándome algo como "¿Qué va a pasar si me suscribo a un observable que se encuentra en un módulo que aún no ha sido cargado de manera diferida?" No estaba seguro, pero él me dijo que la respuesta era utilizar un BSubject, EXACTAMENTE cómo el Sr. Bhadoria lo explicó anteriormente. La respuesta fue usar un BSubject porque siempre devuelve el último valor (al menos así es como recuerdo el comentario final del entrevistador al respecto).
bob.mazzo
1
@ bob.mazzo ¿Por qué necesito usar un BSubject para ese caso? - Si me suscribo a ese Observador, no recibiré nada porque el observador no se ha inicializado, por lo que no puede enviar datos a los observadores y, si uso un BSubject, tampoco recibiré nada por la misma razón. En ambos casos, el suscriptor no recibirá nada porque está dentro de un módulo que no se ha inicializado. Estoy en lo cierto?
Rafael Reyes
183

Observable: resultado diferente para cada observador

Una diferencia muy muy importante. Como Observable es solo una función, no tiene ningún estado, por lo que para cada nuevo Observador, ejecuta el código de creación observable una y otra vez. Esto resulta en:

El código se ejecuta para cada observador. Si es una llamada HTTP, se llama para cada observador

Esto causa errores importantes e ineficiencias.

BehaviorSubject (o Subject) almacena detalles del observador, ejecuta el código solo una vez y da el resultado a todos los 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>

Salida:

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

Observe cómo el uso de resultados Observable.createcreados diferentes para cada observador, pero BehaviorSubjectdio el mismo resultado para todos los observadores. Esto es importante.


Otras diferencias 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
fuente
3
cualquiera que venga a partir KnockoutJS's ko.observable()verá inmediatamente más paralelos a Rx.BehaviorSubjectcomparación conRx.Observable
Simon_Weaver
@Skeptor Observable: el método de suscripción siempre activará el método onNext asociado con el observador y traerá el valor de retorno. BehaviourSubject / Subject: siempre devolverá el último valor en la secuencia. aquí el método de suscripción con el sujeto no se activará en el siguiente método de su Observador hasta que encuentre el último valor en la secuencia.
Mohan Ram
62

Observable y sujeto son ambos observables significa que un observador puede rastrearlos. pero ambos tienen algunas características únicas. Además, hay un total de 3 tipos de temas, cada uno de ellos nuevamente tiene características únicas. Tratemos de entender cada uno de ellos.

Puede encontrar el ejemplo práctico aquí en stackblitz . (Debe verificar la consola para ver la salida real)

ingrese la descripción de la imagen aquí

Observables

Son fríos: el código se ejecuta cuando tienen al menos un solo observador.

Crea una copia de datos: Observable crea una copia de datos para cada observador.

Unidireccional: el observador no puede asignar valor a observable (origen / maestro).

Subject

Están calientes: el código se ejecuta y el valor se transmite incluso si no hay observador.

Datos compartidos: se comparten los mismos datos entre todos los observadores.

bidireccional: el observador puede asignar valor a observable (origen / maestro).

Si está utilizando el sujeto, pierde todos los valores que se transmiten antes de la creación del observador. Así que aquí viene el tema de reproducción

ReplaySubject

Están calientes: el código se ejecuta y el valor se transmite incluso si no hay observador.

Datos compartidos: se comparten los mismos datos entre todos los observadores.

bidireccional: el observador puede asignar valor a observable (origen / maestro). más

Reproduzca el flujo de mensajes: no importa cuándo se suscriba el asunto de reproducción, recibirá todos los mensajes emitidos.

En el asunto y el tema de reproducción no puede establecer el valor inicial en observable. Así que aquí viene el sujeto conductual

BehaviorSubject

Están calientes: el código se ejecuta y el valor se transmite incluso si no hay observador.

Datos compartidos: se comparten los mismos datos entre todos los observadores.

bidireccional: el observador puede asignar valor a observable (origen / maestro). más

Reproduzca el flujo de mensajes: no importa cuándo se suscriba el asunto de reproducción, recibirá todos los mensajes emitidos.

Puede establecer el valor inicial: puede inicializar el observable con el valor predeterminado.

Kedar9444
fuente
3
Vale la pena mencionar que a ReplaySubjecttiene un historial y puede transmitir / emitir una secuencia de valores (antiguos). Solo cuando el búfer se establece en 1 se comporta de manera similar a a BehaviorSubject.
marchita el
28

El objeto Observable representa una colección basada en inserción.

Las interfaces Observador y Observable proporcionan un mecanismo generalizado para la notificación basada en notificaciones, también conocido como el patrón de diseño del observador. El objeto Observable representa el objeto que envía notificaciones (el proveedor); el objeto Observador representa la clase que los recibe (el observador).

La clase Asunto hereda tanto Observable como Observador, en el sentido de que es a la vez observador y observable. Puede usar un tema para suscribir a todos los observadores y luego suscribir el tema a una fuente de datos de fondo

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();

Más en https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md

Md Ayub Ali Sarker
fuente
¿Cuál es la diferencia entre suscripción.disponer () y suscripción.unsubscribe ()?
choopage - Jek Bao
44
@choopage no hay diferencia. este último es el nuevo camino
Royi Namir
Si se da de baja antes de que se elimine el tema, de lo contrario, la suscripción se convierte en basura ya que se suscribe a un valor nulo.
Sophie Zhang
20

Una cosa que no veo en los ejemplos es que cuando lanza BehaviorSubject to Observable a través de asObservable, hereda el comportamiento de devolver el último valor en la suscripción.

Es un poco complicado, ya que a menudo las bibliotecas exponen los campos como observables (es decir, parámetros en ActivatedRoute en Angular2), pero pueden usar Subject o BehaviorSubject detrás de escena. Lo que usan afectaría el comportamiento de la suscripción.

Ver aquí 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
fuente
11

Un observable le permite suscribirse solo mientras que un sujeto permite publicar y suscribirse.

Por lo tanto, un tema permite que sus servicios se utilicen como editor y como suscriptor.

A partir de ahora, no soy tan bueno, Observableasí que compartiré solo un ejemplo de Subject.

Comprendamos mejor con un ejemplo de CLI angular . Ejecute los siguientes comandos:

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Reemplace el contenido de app.component.htmlcon:

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

<app-home>
</app-home>

Ejecute el comando ng g c components/homepara generar el componente de inicio. Reemplace el contenido de home.component.htmlcon:

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

#messagees la variable local aquí. Agregue una propiedad message: string; a la app.component.tsclase 's.

Ejecute este comando ng g s service/message. Esto generará un servicio en src\app\service\message.service.ts. Proporcionar este servicio a la aplicación. .

Importar Subjecta MessageService. Agrega un tema también. El código final se verá así:

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
  }
}

Ahora, inyecte este servicio home.component.tsy pase una instancia del mismo al constructor. Haz esto por app.component.tstambién. Use esta instancia de servicio para pasar el valor de #messagea la función de servicio 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);
  }
}

En el interior app.component.ts, suscríbase y anule la suscripción (para evitar pérdidas de memoria) a 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();
  }
}

Eso es.

Ahora, cualquier valor ingresado dentro #messagede home.component.htmlse imprimirá {{message}}dentroapp.component.html

xameeramir
fuente
¿Por qué la imagen gigante? Si no está directamente relacionado con su respuesta, parece un cebo.
ruffin
@ruffin Esta es solo una respuesta promedio con un número promedio de votos, mira mi perfil. No definitivamente votebait: D
xameeramir
1
Le di un voto a favor antes, pero ha eludido la pregunta de por qué la imagen está ahí. No está directamente relacionado con tu respuesta. No importa si tienes muchas repeticiones o no, si la imagen no es directa y específicamente esclarecedora, te pediría que la elimines . / encogimiento de hombros
ruffin
1
@ruffin Si va en contra del consentimiento de la comunidad, ¡entonces no debería estar allí seguramente!
xameeramir
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
fuente
1

BehaviorSubject vs Observable : RxJS tiene observadores y observables, Rxjs ofrece múltiples clases para usar con flujos de datos, y uno de ellos es BehaviorSubject.

Observables : los observables son colecciones diferidas de múltiples valores a lo largo del tiempo.

BehaviorSubject : Un sujeto que requiere un valor inicial y emite su valor actual a los nuevos suscriptores.

 // 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
Destello
fuente
1

Piense en los Observables como una tubería con agua corriente, a veces el agua fluye y otras no. En algunos casos, es posible que necesite una tubería que siempre tenga agua, puede hacer esto creando una tubería especial que siempre contenga agua, sin importar cuán pequeña sea, llamemos a esta tubería especial BehaviorSubject , si resulta ser un proveedor de suministro de agua en su comunidad, puede dormir tranquilo por la noche sabiendo que su tubería recién instalada simplemente funciona.

En términos técnicos: puede encontrar casos de uso en los que un Observable siempre debe tener valor, tal vez desee capturar el valor de un texto de entrada a lo largo del tiempo, luego puede crear una instancia de BehaviorSubject para garantizar este tipo de comportamiento, digamos:


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

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

Luego puede usar "valor" para muestrear los cambios a lo largo del tiempo.


firstNameChanges.value;

Esto resulta útil cuando combina Observables más adelante, al observar el tipo de su transmisión como BehaviorSubject, puede asegurarse de que la transmisión al menos se dispare o señale al menos una vez .

Ronnel Reposo
fuente