我有一个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的当前值。我该怎么做呢?


当前回答

Subject或Observable没有当前值。当一个值被触发时,它被传递给订阅者,Observable就完成了对它的处理。

如果你想要一个当前值,请使用专门为此目的设计的BehaviorSubject。BehaviorSubject保留最后发出的值,并立即将其发出给新的订阅者。

它还有一个getValue()方法来获取当前值。

其他回答

我在子组件中遇到了同样的问题,最初它必须拥有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

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

最好的方法是使用行为主语,下面是一个例子:

var sub = new rxjs.BehaviorSubject([0, 1])
sub.next([2, 3])
setTimeout(() => {sub.next([4, 5])}, 1500)
sub.subscribe(a => console.log(a)) //2, 3 (current value) -> wait 2 sec -> 4, 5

另一种方法,如果你想/可以使用async await(必须在async函数中),你可以在现代Rxjs中做到这一点:

 async myFunction () {
     const currentValue = await firstValueFrom(
      of(0).pipe(
        withLatestFrom(this.yourObservable$),
        map((tuple) => tuple[1]),
        take(1)
      )
    );
    // do stuff with current value

 }

 

这将发出一个值“Right away”,因为withLatestFrom,然后将解析承诺。

虽然这听起来有点夸张,但这只是另一个“可能”的解决方案,可以保持Observable类型并减少样板文件……

你总是可以创建一个扩展getter来获取Observable的当前值。

要做到这一点,你需要在global.d.t types声明文件中扩展Observable<T>接口。然后在observable.extension.ts文件中实现扩展getter,最后将类型和扩展文件都包含到应用程序中。

你可以参考这个StackOverflow的答案来了解如何将这些扩展包含到你的Angular应用中。

// global.d.ts
declare module 'rxjs' {
  interface Observable<T> {
    /**
     * _Extension Method_ - Returns current value of an Observable.
     * Value is retrieved using _first()_ operator to avoid the need to unsubscribe.
     */
    value: Observable<T>;
  }
}

// observable.extension.ts
Object.defineProperty(Observable.prototype, 'value', {
  get <T>(this: Observable<T>): Observable<T> {
    return this.pipe(
      filter(value => value !== null && value !== undefined),
      first());
  },
});

// using the extension getter example
this.myObservable$.value
  .subscribe(value => {
    // whatever code you need...
  });

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


虽然一个可观察对象确实没有当前值,但通常它会有一个立即可用的值。例如,对于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属性,即使我有一个行为主体。