¿Cómo obtener el valor actual de RxJS Subject u Observable?


Tengo un servicio Angular 2:

import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject}    from 'rxjs/Subject';

@Injectable()
export class SessionStorage extends Storage {
  private _isLoggedInSource = new Subject<boolean>();
  isLoggedIn = this._isLoggedInSource.asObservable();
  constructor() {
    super('session');
  }
  setIsLoggedIn(value: boolean) {
    this.setItem('_isLoggedIn', value, () => {
      this._isLoggedInSource.next(value);
    });
  }
}

Todo funciona muy bien. Pero tengo otro componente que no necesita suscribirse, solo necesita obtener el valor actual de isLoggedIn en un cierto punto en el tiempo. ¿Cómo puedo hacer esto?

Author: Baconbeastnz, 2016-05-07

5 answers

A Subject o Observable no tiene un valor actual. Cuando se emite un valor, se pasa a los suscriptores y el Observable se hace con él.

Si desea tener un valor actual, use BehaviorSubject que está diseñado exactamente para ese propósito. BehaviorSubject mantiene el último valor emitido y lo emite inmediatamente a los nuevos suscriptores.

También tiene un método getValue() para obtener el valor actual.

 173
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-07 14:56:26

La única forma en que debería obtener valores "de" un Observable/Sujeto es con subscribe!

Si estás usando getValue() estás haciendo algo imperativo en paradigma declarativo. Está ahí como una escotilla de escape, pero el 99.9% de las veces NO debes usar getValue(). Hay algunas cosas interesantes que getValue() hará: Lanzará un error si el sujeto se ha dado de baja, evitará que obtenga un valor si el sujeto está muerto porque es errored, etc. Pero, de nuevo, está ahí como una escotilla de escape para circunstancias raras.

Hay varias maneras de obtener el último valor de un Sujeto u Observable de una manera "Rx-y":

  1. Usando BehaviorSubject: Pero en realidad suscribiéndose a él. Cuando se suscriba por primera vez a BehaviorSubject, enviará sincrónicamente el valor anterior con el que recibió o se inicializó.
  2. Usando un ReplaySubject(N): Esto almacenará en caché los valores N y los reproducirá a los nuevos suscriptores.
  3. A.withLatestFrom(B): Utilice este operador para obtener el valor más reciente de observable B cuando observable A emite. Le dará ambos valores en una matriz [a, b].
  4. A.combineLatest(B): este operador para obtener los valores más recientes de A y B cada vez que A o B emite. Le dará ambos valores en una matriz.
  5. shareReplay(): Realiza una multidifusión Observable a través de un ReplaySubject, pero le permite reintentar el observable en caso de error. (Básicamente te da esa promesa de almacenamiento en caché comportamiento).
  6. publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), etc: Otros operadores que aprovechan BehaviorSubject y ReplaySubject. Diferentes sabores de la misma cosa, básicamente multicast la fuente observable canalizando todas las notificaciones a través de un asunto. Necesita llamar a connect() para suscribirse a la fuente con el asunto.
 78
Author: Ben Lesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-26 17:17:46

Tuve una situación similar en la que los suscriptores tardíos se suscriben al Tema después de que llegó su valor.

Encontré ReplaySubject que es similar a BehaviorSubject funciona como un encanto en este caso. Y aquí hay un enlace para una mejor explicación: http://reactivex.io/rxjs/manual/overview.html#replaysubject

 4
Author: Kfir Erez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-13 10:42:28

Me encontré con el mismo problema en los componentes secundarios donde inicialmente tendría que tener el valor actual del Sujeto, luego suscribirse al Sujeto para escuchar los cambios. Solo mantengo el valor actual en el Servicio para que esté disponible para que los componentes accedan, por ejemplo:

import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject}    from 'rxjs/Subject';

@Injectable()
export class SessionStorage extends Storage {

  isLoggedIn: boolean;

  private _isLoggedInSource = new Subject<boolean>();
  isLoggedIn = this._isLoggedInSource.asObservable();
  constructor() {
    super('session');
    this.currIsLoggedIn = false;
  }
  setIsLoggedIn(value: boolean) {
    this.setItem('_isLoggedIn', value, () => {
      this._isLoggedInSource.next(value);
    });
    this.isLoggedIn = value;
  }
}

Un componente que necesita el valor actual podría acceder a él desde el servicio, es decir,:

sessionStorage.isLoggedIn

No estoy seguro de si esta es la práctica correcta:)

 2
Author: Molp Burnbright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-25 06:01:47

Puede almacenar el último valor emitido por separado del Observable. Luego léalo cuando sea necesario.

let lastValue: number;

const subscription = new Service().start();
subscription
    .subscribe((data) => {
        lastValue = data;
    }
);
 -3
Author: Slawa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-05 11:36:44