¿Cuál es la diferencia entre BehaviorSubject y Observable?

12 minutos de lectura

Avatar de usuario de Kevin Mark
kevin marca

Estoy investigando los patrones de diseño de RxJS y no entiendo la diferencia entre BehaviorSubject y Observable.

Según tengo entendido, BehaviorSubject puede contener un valor que puede cambiar. Se puede suscribir y los suscriptores pueden recibir valores actualizados. Ambos parecen tener exactamente el mismo propósito.

  1. ¿Cuándo se debe usar Observable vs BehaviorSubject y viceversa?
  2. ¿Cuáles son los beneficios de usar BehaviorSubject en lugar de Observable y viceversa?

  • Este artículo me ayudó especialmente a comprender los observables frente a los sujetos frente a los sujetos de comportamiento en la forma ELI5. javascript.plainenglish.io/…

    – Mike.Álvarez

    31 de agosto de 2021 a las 15:06

Avatar de usuario de Shantanu Bhadoria
Shantanu Bhadoria

BehaviorSubject es una variante de Subjectun tipo de Observable al que uno puede “suscribirse” como cualquier otro Observable.

Características de BehaviorSubject

  • Necesita un valor inicial ya que siempre debe devolver un valor al momento de la suscripción, incluso si no ha recibido el método next()
  • Tras la suscripción, devuelve el último valor del Asunto. Un Observable normal solo se activa cuando recibe el método onNext()
  • En cualquier momento, uno puede recuperar el último valor del Sujeto en un no Observable usando el método getValue()

Características del tema

  • El sujeto es un “observador” además de ser un Observable; por lo tanto, también se pueden enviar valores a un Sujeto mientras se está suscrito a él
  • Uno puede obtener un valor de BehaviorSubject usando el método asObservable()

Ejemplo 1 usando BehaviorSubject

// BehaviorSubject.
// 'A' is an initial value. If there is a Subscription 
// after it, it would immediately get the value 'A'.

beSubject = new BehaviorSubject('a'); 

beSubject.next('b');

beSubject.subscribe(value => {
  console.log('Subscription received the value ', value);

  // Subscription received B. It would not happen
  // for an Observable or Subject by default.
});

beSubject.next('c');
// Subscription received C.

beSubject.next('d');
// Subscription received D.

Ejemplo 2 usando Asunto

// Subject.

subject = new Subject(); 

subject.next('b');

subject.subscribe(value => {
  console.log('Subscription received the value ', value);

  // Subscription won't receive anything at this point.
});

subject.next('c');
// Subscription received C.

subject.next('d');
// Subscription received D.

Se puede crear un Observable a partir de ambos Subject y BehaviorSubject; Por ejemplo, subjectName.asObservable().

La única diferencia es que no se pueden enviar valores a un Observable usando el método next().

En Angular, se recomienda usar BehaviorSubject para transferir datos como un servicio a menudo se inicializa antes que un componente.

BehaviorSubject garantiza que el componente que consume el Servicio reciba los últimos datos actualizados, incluso si no hay nuevas actualizaciones, debido a la suscripción del componente al Servicio.

  • Estoy un poco confundido con el ejemplo 2 del tema regular. ¿Por qué la suscripción no obtendrá nada aunque en la segunda línea envíe valores al sujeto usando subject.next(“b”)?

    – jmod999

    11/11/2016 a las 17:45

  • @jmod999 El segundo ejemplo es un sujeto regular que recibe un valor justo antes de que se llame a la suscripción. En asuntos regulares, la suscripción solo se activa para los valores recibidos después de que se llama la suscripción. Como se recibe justo antes de la suscripción, no se envía a la suscripción.

    – Shantanu Bhadoria

    19/04/2017 a las 19:20

  • Tuve una entrevista con Angular 4 el miércoles. Como todavía estoy aprendiendo sobre la nueva plataforma, me hizo tropezar preguntándome algo como “¿Qué sucederá si me suscribo a un observable que está en un módulo que aún no se ha cargado de forma diferida?” No estaba seguro, pero me dijo que la respuesta era usar un BSubject, EXACTAMENTE como lo explicó el Sr. Bhadoria arriba. La respuesta fue usar un BSubject porque siempre devuelve el valor más reciente (al menos así es como recuerdo el comentario final del entrevistador al respecto).

    – bob.mazzo

    24 de noviembre de 2017 a las 15:48


  • @bob.mazzo ¿Por qué necesito usar un BSubject para ese caso? — Si me suscribo a ese Observer, 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 ha sido inicializado. ¿Tengo razón?

    –Rafael Reyes

    13 de junio de 2018 a las 23:03

  • ¿No debería el servicio tener un privado? BehaviourSubject y el valor se accede desde un público Observable que emite el valor de la BehaviourSubjectpara no permitir next siendo llamado en el BS fuera del servicio?

    – Emobe

    5 de junio de 2019 a las 10:08

Avatar de usuario de Vamshi
Vamshi

Observable: resultado diferente para cada observador

Una diferencia muy muy importante. Dado que Observable es solo una función, no tiene ningún estado, por lo que para cada nuevo Observer, 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 provoca importantes errores e ineficiencias.

BehaviorSubject (o Asunto) almacena los detalles del observador, ejecuta el código solo una vez y proporciona el resultado a todos los observadores.

Ex:

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

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

Producción :

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

Observa cómo usando Observable.create creó una salida diferente para cada observador, pero BehaviorSubject dio la misma salida 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                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

  • cualquiera que venga de KnockoutJS's ko.observable() inmediatamente verá más paralelismos con Rx.BehaviorSubject en comparación con Rx.Observable

    – Simón_Weaver

    21 de junio de 2017 a las 21:57

  • @Skeptor Observable: El método subscribe siempre activará el método onNext asociado con el observador y traerá el valor de retorno. ComportamientoAsunto/Asunto: Siempre devolverá el valor más reciente en la transmisión. aquí el método de suscripción con el sujeto no activará el método Siguiente de su observador hasta que encuentre el valor más reciente en la transmisión.

    – Mohan Ram

    26 de octubre de 2017 a las 10:58


Avatar de usuario de Kedar9444
cedar9444

Observable y Sujeto Ambos son observables, lo que significa que un observador puede rastrearlos. Ambos tienen algunas características únicas. Hay 3 tipos de Sujetos, cada uno de los cuales también tiene características únicas.

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

ingrese la descripción de la imagen aquí

Observables

Están fríos: El código se ejecuta cuando tienen al menos un único observador.

Crea una copia de los datos: Observable crea una copia de los 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 un observador.

Comparte datos: Los mismos datos se comparten entre todos los observadores.

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

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

ReplaySubject

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

Comparte datos: Los mismos datos se comparten entre todos los observadores.

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

Reproducir el flujo de mensajes: No importa cuándo te suscribas al asunto de la repetición, recibirás todos los mensajes transmitidos.

En Subject y ReplaySubject, no puede establecer el valor inicial en observable. Así que aquí viene Sujeto conductual

BehaviorSubject

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

Comparte datos: Los mismos datos se comparten entre todos los observadores.

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

Reproducir el flujo de mensajes: No importa cuándo te suscribas al asunto de la repetición, recibirás todos los mensajes transmitidos.

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

  • Podría valer la pena mencionar que un ReplaySubject tiene 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 un BehaviorSubject.

    – Marchitez

    19 de noviembre de 2019 a las 8:29

  • Para BehaviorSubject, el párrafo “Reproducir el flujo de mensajes” parece no ser correcto

    – Jaqen H´ghar

    19 de diciembre de 2020 a las 7:48

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

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

La clase Sujeto hereda tanto Observable como Observer, en el sentido de que es tanto un observador como un observable. Puede usar un tema para suscribir a todos los observadores y luego suscribir el tema a una fuente de datos 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();

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

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

Es la parte complicada, 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 suscripción.

Mira aquí http://jsbin.com/ziquxapubo/edit?html,js,consola

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

Un observable le permite suscribirse sólo mientras que un sujeto le permite tanto publicar como suscribirse.

Así que un sujeto le permite a su servicios para ser utilizado como editor y suscriptor.

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

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

npm install -g @angular/cli

ng new angular2-subject

cd angular2-subject

ng serve

Reemplazar el contenido de app.component.html con:

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

<app-home>
</app-home>

Ejecute el comando ng g c components/home para generar el componente de inicio. Reemplazar el contenido de home.component.html con:

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

#message es la variable local aquí. Agregar una propiedad message: string;
hacia app.component.ts‘clase S.

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

Importar Subject en 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 en home.component.ts y pasar una instancia de él al constructor. haz esto por app.component.ts también. Use esta instancia de servicio para pasar el valor de #message a 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);
  }
}

Adentro app.component.tssuscríbase y cancele la suscripción (para evitar pérdidas de memoria) al 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 todo.

Ahora, cualquier valor ingresado dentro #message de home.component.html se imprimirá a {{message}} adentro app.component.html

Avatar de usuario de R4L_R4o
R4L_R4o

Pensar en observables como una tubería con agua que fluye, a veces el agua fluye ya veces no. En algunos casos, es posible que necesite una tubería que siempre tenga agua, puede hacerlo creando una tubería especial que siempre contenga agua sin importar cuán pequeña sea, llamemos a esta tubería especial ComportamientoAsuntosi usted es 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 debería 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");

A continuación, puede utilizar “valor” para probar los cambios a lo largo del tiempo.


firstNameChanges.value;

Esto es útil cuando combina Observables más tarde, al observar el tipo de su transmisión como BehaviorSubject, puede asegurarse de que el transmita al menos incendios o señale solo una vez al menos.

  • cubre muchas partes, pero el lado positivo de su explicación es que brinda una analogía fácil de entender, ¡Kudo!

    – Jeb50

    2 abr 2021 a las 21:59

¿Ha sido útil esta solución?