Asunto vs BehaviorSubject vs ReplaySubject en Angular

122

He estado buscando entender esos 3:

Asunto , Asunto de comportamiento y Asunto de repetición . Me gustaría usarlos y saber cuándo y por qué, cuáles son los beneficios de usarlos y, aunque he leído la documentación, he visto tutoriales y he buscado en Google, no he podido entender esto.

Entonces, ¿cuál es su propósito? Un caso del mundo real sería muy apreciado porque ni siquiera tiene que codificar.

Preferiría una explicación clara, no solo "a + b => c al que estás suscrito ...."

Gracias

Paul Samsotha
fuente
1
Ya hay una pregunta con comportamiento sujeto con observable; stackoverflow.com/questions/39494058/… y la documentación sobre el tema de la reproducción es clara imo github.com/Reactive-Extensions/RxJS/blob/master/doc/api/…
eko
Hay una presentación relativamente completa de los temas en Rxjs en esta respuesta , que complementa muy bien la respuesta de peeksilet. Esto también incluye detalles importantes sobre el comportamiento después del despido, por lo que es bueno echar un vistazo.
user3743222

Respuestas:

277

Realmente se reduce al comportamiento y la semántica. Con un

  • Subject- un suscriptor solo obtendrá valores publicados que se emitieron después de la suscripción. Pregúntese, ¿es eso lo que quiere? ¿El suscriptor necesita saber algo sobre valores anteriores? De lo contrario, puede usar esto, de lo contrario, elija uno de los otros. Por ejemplo, con comunicación de componente a componente. Supongamos que tiene un componente que publica eventos para otros componentes con un clic de botón. Puede utilizar un servicio con un tema para comunicarse.

  • BehaviorSubject- el último valor se almacena en caché. Un suscriptor obtendrá el valor más reciente con la suscripción inicial. La semántica de este tema es representar un valor que cambia con el tiempo. Por ejemplo, un usuario que inició sesión. El usuario inicial puede ser un usuario anónimo. Pero una vez que un usuario inicia sesión, el nuevo valor es el estado del usuario autenticado.

    El BehaviorSubjectse inicializa con un valor inicial. A veces, esto es importante para la preferencia de codificación. Digamos, por ejemplo, que lo inicializa con un null. Luego, en su suscripción, debe hacer una verificación nula. Quizás está bien, o quizás molesto.

  • ReplaySubject- Puede almacenar en caché hasta un número específico de emisiones. Los suscriptores obtendrán todos los valores almacenados en caché al suscribirse. ¿Cuándo necesitarías este comportamiento? Honestamente, no he tenido ninguna necesidad de tal comportamiento, excepto en el siguiente caso:

    Si inicializa a ReplaySubjectcon un tamaño de búfer de 1, entonces en realidad se comporta como a BehaviorSubject. El último valor siempre se almacena en caché, por lo que actúa como un valor que cambia con el tiempo. Con esto, no hay necesidad de una nullverificación como en el caso de los BehaviorSubjectinicializados con un null. En este caso, nunca se emite ningún valor al suscriptor hasta la primera publicación.

Así que todo se reduce al comportamiento que esperas (en cuanto a cuál usar). La mayor parte del tiempo probablemente querrá utilizar un BehaviorSubjectporque lo que realmente quiere representar es esa semántica de "valor en el tiempo". Pero personalmente no veo nada malo en la sustitución de ReplaySubjectinicializado con 1.

Lo que desea evitar es usar la vainilla Subjectcuando lo que realmente necesita es un comportamiento de almacenamiento en caché. Tomemos, por ejemplo, que está escribiendo un guardia de ruta o una resolución. Obtienes algunos datos en esa protección y los configuras en un servicio Subject. Luego en el componente enrutado te suscribes al servicio sujeto para intentar sacar ese valor que se emitió en la guardia. OOP. ¿Dónde está el valor? Ya fue emitido, DUH. Utilice un tema de "almacenamiento en caché".

Ver también:

Paul Samsotha
fuente
1
Esto es breve y fácil de entender las diferencias. Cuando el valor cambia en el servicio y los componentes también cambian que se muestra el valor, BehaviourSubjects o Replay Subject es la solución.
Saiyaff Farouk
1
¡Gracias! ReplaySubjectcon un tamaño de búfer de 1 era exactamente lo que necesitaba. Tenía un guardia de ruta que necesitaba el valor, pero necesitaba esperar la primera emisión. Entonces, BehaviorSubjectno lo estaba cortando, ya que no quería un valor inicial ( nulltampoco funcionaría porque lo estaba usando para significar un estado)
menehune23
1
@ menehune23 También necesitaba ReplaySubject para una resolveclase de guardia angular . Mi servicio de datos podría ser asíncrono o síncrono (si los datos ya se habían recuperado). Si era sincrónico, el Subject.next () se activaba antes de que la resolvefunción hubiera regresado y Angular se suscribiera internamente. BehaviourSubject posiblemente funcionaría, pero tendría que llamar explícitamente complete()y también agregar nullcheques para el valor inicial. Lo que funcionó fue nuevo ReplaySubject<DataType>(1) yresolveSubject.asObservable().take(1).map(....)
Drenai
1
Estoy usando un ReplaySubject con un tamaño de búfer de 1, pero por alguna razón cuando obtengo un Observable con .asObservable()el Observable, envío un valor de nulla los suscriptores antes de llamar next()a mi ReplaySubject. Pensé que no se suponía que tuviera un valor inicial a diferencia de BehaviorSubject.
Kyle V.
2
Creo que un ejemplo bastante sencillo que podrías mencionar para el tema de la repetición sería para una "sala de chat" o un escenario en el lobby del juego donde quieres que los nuevos miembros vean los últimos 10 mensajes.
James
15

Un resumen útil de los diferentes tipos observables, nombres no intuitivos que sé jajaja .

  • Subject - Un suscriptor solo obtendrá los valores publicados después de que se haya realizado la suscripción.
  • BehaviorSubject - Los nuevos suscriptores obtienen el último valor publicado O el valor inicial inmediatamente después de la suscripción.
  • ReplaySubject - Los nuevos suscriptores obtienen todos los valores publicados anteriormente inmediatamente después de la suscripción
Ricky Boyce
fuente
1-n valores publicados? Entonces, si hubiera 2 valores publicados, un ReplaySubject produciría -1 valores publicados.
Jason Cheng
@JasonCheng no, recupera todos los valores publicados anteriormente al suscribirse, actualice la respuesta :)
Ricky Boyce
11
  1. Tema : Al suscribirse, siempre obtiene los datos que se envían después de su suscripción, es decir , no se reciben los valores anteriores .
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);

Con este ejemplo, aquí está el resultado que se imprimirá en la consola:

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

Tenga en cuenta cómo las suscripciones que llegan tarde se pierden algunos de los datos que se han introducido en el tema.

  1. Repetir temas : puede ayudar manteniendo un búfer de valores anteriores que se emitirán a las nuevas suscripciones.

A continuación, se muestra un ejemplo de uso para reproducir sujetos en los que buffer of 2 previous values se mantienen y se emiten en nuevas suscripciones:

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

Esto es lo que nos brinda en la consola:

From 1st sub: 3
From 1st sub: 4
From 1st sub: 5
From 2nd sub: 4
From 2nd sub: 5
  1. Sujetos de conducta : son similares a los sujetos de repetición, pero volverán a emitir solo el último valor emitido, o un valor predeterminado si no se ha emitido ningún valor 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);
});

Y el resultado:

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

Referencia: https://alligator.io/rxjs/subjects/

Varun Sukheja
fuente
4

De: libro de Randall Koutnik "Construya sitios web reactivos con RxJS". :

Un sujeto es un objeto que es un observable turboalimentado. En esencia, un Sujeto actúa como un observable regular, pero cada suscripción está conectada a la misma fuente. Los sujetos también son observadores y tienen métodos de siguiente, error y hecho para enviar datos a todos los suscriptores a la vez. Porque los sujetos son observadores, se pueden pasar directamente a una llamada de suscripción y todos los eventos del observable original se enviarán a través del sujeto a sus suscriptores.

Podemos usar ReplaySubject para rastrear el historial. UNA ReplaySubject registra los últimos n eventos y los devuelve a cada nuevo suscriptor. Por ejemplo, en la aplicación de chat. Podemos usarlo para rastrear el registro del historial de chat anterior.

Un BehaviorSubject es una versión simplificada de ReplaySubject . El ReplaySubject almacenó un número arbitrario de eventos, el BehaviorSubject solo registra el valor del último evento. Siempre que un BehaviorSubject registra una nueva suscripción, emite el último valor al suscriptor, así como los nuevos valores que se le pasan. BehaviorSubject es útil cuando se trata de unidades de estado individuales, como opciones de configuración.

HS Progr
fuente
1

La respuesta más votada es claramente incorrecta al afirmar que:

"Si inicializa a ReplaySubjectcon un tamaño de búfer de 1, en realidad se comporta como un BehaviorSubject"


Esto no es del todo cierto; Consulte esta excelente publicación de blog sobre las diferencias entre esos dos. Por ejemplo, si se suscribe a un completo BehaviorSubject, no recibirá el último valor, pero por unReplaySubject(1) , recibirá el último valor.

Esta es una diferencia importante que no debe pasarse por alto:

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

Consulte este ejemplo de código aquí que proviene de otra excelente publicación de blog sobre el tema.

Marchitar
fuente
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")
  • Asunto: un suscriptor solo obtendrá los valores publicados después de que se haya realizado la suscripción.
  • BehaviorSubject: los nuevos suscriptores obtienen el último valor publicado O el valor inicial inmediatamente después de la suscripción.
Pramod Patil
fuente