我有一个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);
    });
  }
}

一切都很好。但是我有另一个不需要订阅的组件,它只需要在某个时间点获得isLoggedIn的当前值。我该怎么做呢?


当前回答

一个类似的答案被否决了。但我认为我可以证明我在这里提出的建议适用于有限的情况。


虽然一个可观察对象确实没有当前值,但通常它会有一个立即可用的值。例如,对于redux / flux / akita存储,您可以根据一些可观察的数据从中央存储请求数据,这些值通常会立即可用。

如果是这种情况,那么当您订阅时,该值将立即返回。

假设你有一个对服务的调用,在完成时你想从你的商店中获得一些东西的最新值,这可能不会发出:

你可以尝试这样做(你应该尽可能地把事情“放在管道里”):

 serviceCallResponse$.pipe(withLatestFrom(store$.select(x => x.customer)))
                     .subscribe(([ serviceCallResponse, customer] => {

                        // we have serviceCallResponse and customer 
                     });

这样做的问题是它会阻塞,直到第二个可观察对象发出一个值,而这可能永远不会发生。

我发现自己最近只需要在一个值立即可用的情况下评估一个可观察到的值,更重要的是,我需要能够检测到它是否可用。我最后是这样做的:

 serviceCallResponse$.pipe()
                     .subscribe(serviceCallResponse => {

                        // immediately try to subscribe to get the 'available' value
                        // note: immediately unsubscribe afterward to 'cancel' if needed
                        let customer = undefined;

                        // whatever the secondary observable is
                        const secondary$ = store$.select(x => x.customer);

                        // subscribe to it, and assign to closure scope
                        sub = secondary$.pipe(take(1)).subscribe(_customer => customer = _customer);
                        sub.unsubscribe();

                        // if there's a delay or customer isn't available the value won't have been set before we get here
                        if (customer === undefined) 
                        {
                           // handle, or ignore as needed
                           return throwError('Customer was not immediately available');
                        }
                     });

请注意,对于以上所有内容,我都使用subscribe来获取值(正如@Ben讨论的那样)。没有使用.value属性,即使我有一个行为主体。

其他回答

有两种方法可以实现这一点。

BehaviorSubject有一个getValue()方法,您可以在特定的时间点获取值。 你可以直接订阅BehaviorSubject,也可以将订阅的值传递给类成员、字段或属性。

我不会同时推荐这两种方法。

在第一种方法中,它是一种方便的方法,您可以随时获取值,您可以将其称为该时间点的当前快照。问题是你可以在你的代码中引入竞争条件,你可以在许多不同的地方和不同的时间调用这个方法,这很难调试。

第二种方法是大多数开发人员在他们想要订阅时使用的原始值,您可以跟踪订阅,以及何时取消订阅以避免进一步的内存泄漏,如果您真的非常想将其绑定到一个变量,并且没有其他方法来连接它,则可以使用这种方法。

我建议,再看看你的用例,你在哪里使用它?例如,当你调用任何API时,你想要确定用户是否登录,你可以结合其他观察数据:

const data$ = apiRequestCall$().pipe(
 // Latest snapshot from BehaviorSubject.
 withLatestFrom(isLoggedIn),
 // Allow call only if logged in.
 filter(([request, loggedIn]) => loggedIn)
 // Do something else..
);

在angular的情况下,你可以通过管道数据$ | async直接使用它到UI。

你可以将最后一个发出的值与Observable分开存储。然后在需要的时候阅读它。

let lastValue: number;

const subscription = new Service().start();
subscription
    .subscribe((data) => {
        lastValue = data;
    }
);

唯一的方法,你应该得到值“出”一个可观察对象/主题是与订阅!

如果你在使用getValue()你在做一些声明式的命令。它是一个逃生口,但是99.9%的情况下你不应该使用getValue()。getValue()会做一些有趣的事情:如果主题已经取消订阅,它会抛出一个错误,如果主题已经死亡,它会阻止你获得一个值,因为它是错误的,等等。但是,再一次地,它是在罕见情况下的一个逃生口。

有几种方法可以以“Rx-y”方式从Subject或Observable中获取最新的值:

Using BehaviorSubject: But actually subscribing to it. When you first subscribe to BehaviorSubject it will synchronously send the previous value it received or was initialized with. Using a ReplaySubject(N): This will cache N values and replay them to new subscribers. A.withLatestFrom(B): Use this operator to get the most recent value from observable B when observable A emits. Will give you both values in an array [a, b]. A.combineLatest(B): Use this operator to get the most recent values from A and B every time either A or B emits. Will give you both values in an array. shareReplay(): Makes an Observable multicast through a ReplaySubject, but allows you to retry the observable on error. (Basically it gives you that promise-y caching behavior). publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), etc: Other operators that leverage BehaviorSubject and ReplaySubject. Different flavors of the same thing, they basically multicast the source observable by funneling all notifications through a subject. You need to call connect() to subscribe to the source with the subject.

我在子组件中遇到了同样的问题,最初它必须拥有Subject的当前值,然后订阅Subject以侦听更改。我只是在服务中维护当前值,以便组件可以访问它,例如:

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

需要当前值的组件可以直接从服务中访问它,即:

sessionStorage.isLoggedIn

不确定这是否是正确的做法:)

一个类似的答案被否决了。但我认为我可以证明我在这里提出的建议适用于有限的情况。


虽然一个可观察对象确实没有当前值,但通常它会有一个立即可用的值。例如,对于redux / flux / akita存储,您可以根据一些可观察的数据从中央存储请求数据,这些值通常会立即可用。

如果是这种情况,那么当您订阅时,该值将立即返回。

假设你有一个对服务的调用,在完成时你想从你的商店中获得一些东西的最新值,这可能不会发出:

你可以尝试这样做(你应该尽可能地把事情“放在管道里”):

 serviceCallResponse$.pipe(withLatestFrom(store$.select(x => x.customer)))
                     .subscribe(([ serviceCallResponse, customer] => {

                        // we have serviceCallResponse and customer 
                     });

这样做的问题是它会阻塞,直到第二个可观察对象发出一个值,而这可能永远不会发生。

我发现自己最近只需要在一个值立即可用的情况下评估一个可观察到的值,更重要的是,我需要能够检测到它是否可用。我最后是这样做的:

 serviceCallResponse$.pipe()
                     .subscribe(serviceCallResponse => {

                        // immediately try to subscribe to get the 'available' value
                        // note: immediately unsubscribe afterward to 'cancel' if needed
                        let customer = undefined;

                        // whatever the secondary observable is
                        const secondary$ = store$.select(x => x.customer);

                        // subscribe to it, and assign to closure scope
                        sub = secondary$.pipe(take(1)).subscribe(_customer => customer = _customer);
                        sub.unsubscribe();

                        // if there's a delay or customer isn't available the value won't have been set before we get here
                        if (customer === undefined) 
                        {
                           // handle, or ignore as needed
                           return throwError('Customer was not immediately available');
                        }
                     });

请注意,对于以上所有内容,我都使用subscribe来获取值(正如@Ben讨论的那样)。没有使用.value属性,即使我有一个行为主体。