BehaviorSubject vs Observable?

Estoy investigando patrones Angular RxJs y no entiendo la diferencia entre un BehaviorSubject y un Observable .

Desde mi punto de vista, 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 versus un BehaviorSubject ? ¿Hay beneficios al usar un BehaviorSubject sobre un Observable o viceversa?

BehaviorSubject es un tipo de tema, un tema es un tipo especial de observable, por lo que puede 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 punto, 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.

Además, puede obtener un observable del sujeto de comportamiento 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 usando subject.asObservable() .

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

En 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.

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 Observer nuevo, 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 e ineficiencias importantes

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

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

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

La clase Asunto hereda Observable y Observer, en el sentido de que es tanto un observador como un observable. Puede usar un sujeto para suscribir a todos los observadores, y luego suscribir el sujeto a una fuente de datos 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 echas BehaviorSubject a Observable a través de asObservable, hereda el comportamiento de devolver el último valor en la suscripción.

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

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

Sujeto en Angular

cortesía

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

Por lo tanto, un tema permite que sus servicios se utilicen como publicadores y suscriptores.

Por el momento, 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:

 
{{message}}

Ejecute el comando ng gc components/home para generar el componente de inicio. Reemplace el contenido de home.component.html con:

   

#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 . Proporcione este servicio a la aplicación .

Importar Subject a MessageService . Agrega un sujeto 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(); 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 de este al constructor. Haga esto también para app.component.ts . Utilice 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 memory leaks) 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 de #message de home.component.html se imprimirá en {{message}} dentro de app.component.html