example - observables angular 4
BehaviorSubject vs Observable? (8)
Estoy investigando los patrones de Angular RxJs y no entiendo la diferencia entre un
BehaviorSubject
y un
Observable
.
Según tengo entendido, un
BehaviorSubject
es 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
Observable
un
Observable
vs un
BehaviorSubject
?
¿Existen beneficios al usar un
BehaviorSubject
sobre un
Observable
o viceversa?
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
Observable.create
creó resultados diferentes para cada observador, pero
BehaviorSubject
dio 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 │
└─────────────────────────────────────┴─────────────────────────────────────┘
El objeto Observable representa una colección basada en inserción.
Las interfaces de observador y observable proporcionan un mecanismo generalizado para notificaciones basadas en notificaciones, también conocido como patrón de diseño de 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
Un observable permite suscribirse solo, mientras que un subject permite publicar y suscribirse.
Por lo tanto, un tema permite que sus services se utilicen como editor y como suscriptor.
A partir de ahora, no soy tan bueno en
Observable
así 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.html
con:
<div *ngIf="message">
{{message}}
</div>
<app-home>
</app-home>
Ejecute el comando
ng gc components/home
para generar el componente de inicio.
Reemplace 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í.
Agregue un
message: string;
propiedad
message: string;
a la clase de
app.component.ts
.
Ejecute este comando
ng gs service/message
.
Esto generará un servicio en
src/app/service/message.service.ts
.
Proporcionar
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 pase una instancia al constructor.
Haga esto también para
app.component.ts
.
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);
}
}
Dentro de
app.component.ts
, suscríbase y anule 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.
Ahora, cualquier valor ingresado dentro del
#message
de
home.component.html
se imprimirá en
{{message}}
dentro de
app.component.html
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);
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 un
onnext
-
en cualquier momento, puede recuperar el último valor del sujeto en un código no observable utilizando el método
getValue()
.
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 de comportamiento observable utilizando el método
asObservable()
en
BehaviorSubject
.
Observable
es un genérico, y
BehaviorSubject
es técnicamente 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
Subject
y
BehaviorSubject
mediante
subject.asObservable()
.
La única diferencia es que no puede enviar valores a un observable utilizando el método
next()
.
En los servicios angulares, usaría
BehaviorSubject
para 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.
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
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)
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 repetició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.
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
});