简体   繁体   English

如何获取 RxJS Subject 或 Observable 的当前值?

[英]How to get current value of RxJS Subject or Observable?

I have an Angular 2 service:我有一个 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);
    });
  }
}

Everything works great.一切都很好。 But I have another component which doesn't need to subscribe, it just needs to get the current value of isLoggedIn at a certain point in time.但是我有另一个不需要订阅的组件,它只需要在某个时间点获取 isLoggedIn 的当前值。 How can I do this?我怎样才能做到这一点?

A Subject or Observable doesn't have a current value. SubjectObservable没有当前值。 When a value is emitted, it is passed to subscribers and the Observable is done with it.当一个值被发出时,它被传递给订阅者, Observable就用它完成了。

If you want to have a current value, use BehaviorSubject which is designed for exactly that purpose.如果您想获得当前值,请使用专为此目的而设计的BehaviorSubject BehaviorSubject keeps the last emitted value and emits it immediately to new subscribers. BehaviorSubject保留最后发出的值并立即将其发送给新订阅者。

It also has a method getValue() to get the current value.它还有一个方法getValue()来获取当前值。

The only way you should be getting values "out of" an Observable/Subject is with subscribe!应该从 Observable/Subject 中“获取”值的唯一方法是订阅!

If you're using getValue() you're doing something imperative in declarative paradigm.如果您正在使用getValue()那么您就是在声明式范式中做一些必要的事情。 It's there as an escape hatch, but 99.9% of the time you should NOT use getValue() .它是一个逃生舱,但在99.9% 的情况下你不应该使用getValue() There are a few interesting things that getValue() will do: It will throw an error if the subject has been unsubscribed, it will prevent you from getting a value if the subject is dead because it's errored, etc. But, again, it's there as an escape hatch for rare circumstances. getValue()会做一些有趣的事情:如果主题已取消订阅,它将抛出错误,如果主题因错误而死亡,它将阻止您获取值等。但是,再次,它在那里作为罕见情况下的逃生舱口。

There are several ways of getting the latest value from a Subject or Observable in a "Rx-y" way:有几种方法可以以“Rx-y”方式从 Subject 或 Observable 获取最新值:

  1. Using BehaviorSubject : But actually subscribing to it .使用BehaviorSubject :但实际上订阅了它 When you first subscribe to BehaviorSubject it will synchronously send the previous value it received or was initialized with.当您第一次订阅BehaviorSubject ,它将同步发送它接收到或初始化时使用的先前值。
  2. Using a ReplaySubject(N) : This will cache N values and replay them to new subscribers.使用ReplaySubject(N) :这将缓存N值并将它们重播给新订阅者。
  3. A.withLatestFrom(B) : Use this operator to get the most recent value from observable B when observable A emits. A.withLatestFrom(B) :当 observable A发出时,使用此运算符从 observable B获取最新值。 Will give you both values in an array [a, b] .将在数组[a, b]为您提供两个值。
  4. A.combineLatest(B) : Use this operator to get the most recent values from A and B every time either A or B emits. A.combineLatest(B) :每次AB发出时,使用此运算符从AB获取最新值。 Will give you both values in an array.会给你一个数组中的两个值。
  5. shareReplay() : Makes an Observable multicast through a ReplaySubject , but allows you to retry the observable on error. shareReplay() :通过ReplaySubject进行 Observable 多播,但允许您在出错时重试 observable。 (Basically it gives you that promise-y caching behavior). (基本上它为您提供了承诺的缓存行为)。
  6. publishReplay() , publishBehavior(initialValue) , multicast(subject: BehaviorSubject | ReplaySubject) , etc: Other operators that leverage BehaviorSubject and ReplaySubject . publishReplay()publishBehavior(initialValue)multicast(subject: BehaviorSubject | ReplaySubject)等:利用BehaviorSubjectReplaySubject其他操作符。 Different flavors of the same thing, they basically multicast the source observable by funneling all notifications through a subject.同一事物的不同风格,它们基本上通过将所有通知汇集到一个主题来多播源 observable。 You need to call connect() to subscribe to the source with the subject.您需要调用connect()以订阅带有主题的源。

I had similar situation where late subscribers subscribe to the Subject after its value arrived.我遇到过类似的情况,迟到的订阅者在它的价值到达后订阅它。

I found ReplaySubject which is similar to BehaviorSubject works like a charm in this case.我发现与 BehaviorSubject 类似的ReplaySubject在这种情况下就像一个魅力。 And here is a link to better explanation: http://reactivex.io/rxjs/manual/overview.html#replaysubject这是更好解释的链接: http : //reactivex.io/rxjs/manual/overview.html#replaysubject

const observable = of('response')

function hasValue(value: any) {
  return value !== null && value !== undefined;
}

function getValue<T>(observable: Observable<T>): Promise<T> {
  return observable
    .pipe(
      filter(hasValue),
      first()
    )
    .toPromise();
}

const result = await getValue(observable)
// Do the logic with the result
// .................
// .................
// .................

You can check the full article on how to implement it from here.您可以从此处查看有关如何实现它的完整文章。 https://www.imkrish.com/blog/development/simple-way-get-value-from-observable https://www.imkrish.com/blog/development/simple-way-get-value-from-observable

I encountered the same problem in child components where initially it would have to have the current value of the Subject, then subscribe to the Subject to listen to changes.我在子组件中遇到了同样的问题,最初它必须具有主题的当前值,然后订阅主题以听取更改。 I just maintain the current value in the Service so it is available for components to access, eg :我只是维护服务中的当前值,以便组件可以访问它,例如:

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

A component that needs the current value could just then access it from the service, ie,:需要当前值的组件可以从服务中访问它,即:

sessionStorage.isLoggedIn

Not sure if this is the right practice :)不确定这是否是正确的做法:)

A similar looking answer was downvoted.类似的寻找答案downvoted。 But I think I can justify what I'm suggesting here for limited cases.但我想我可以证明我在这里建议的有限情况是合理的。


While it's true that an observable doesn't have a current value, very often it will have an immediately available value.虽然 observable 确实没有当前值,但它通常具有立即可用的值。 For example with redux / flux / akita stores you may request data from a central store, based on a number of observables and that value will generally be immediately available.例如,对于 redux/flux/akita 商店,您可以从中央商店请求数据,基于许多可观察的数据,并且该值通常会立即可用。

If this is the case then when you subscribe , the value will come back immediately.如果是这种情况,那么当您subscribe ,该值将立即返回。

So let's say you had a call to a service, and on completion you want to get the latest value of something from your store, that potentially might not emit :因此,假设您有一个服务调用,并且在完成后您想从您的商店中获取某物的最新值,这可能不会发出

You might try to do this (and you should as much as possible keep things 'inside pipes'):您可能会尝试这样做(并且您应该尽可能将东西放在“管道内”):

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

                        // we have serviceCallResponse and customer 
                     });

The problem with this is that it will block until the secondary observable emits a value, which potentially could be never.这样做的问题是它会阻塞,直到辅助 observable 发出一个值,这可能永远不会。

I found myself recently needing to evaluate an observable only if a value was immediately available , and more importantly I needed to be able to detect if it wasn't.我发现自己最近只需要在一个值立即可用时才需要评估一个 observable ,更重要的是我需要能够检测它是否不是。 I ended up doing this:我最终这样做了:

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

Note that for all of the above I'm using subscribe to get the value (as @Ben discusses).请注意,对于上述所有内容,我使用subscribe来获取值(如@Ben 讨论的那样)。 Not using a .value property, even if I had a BehaviorSubject .不使用.value属性,即使我有一个BehaviorSubject

Although it may sound overkill, this is just another "possible" solution to keep Observable type and reduce boilerplate...尽管这听起来有些矫枉过正,但这只是保持Observable类型和减少样板文件的另一种“可能”解决方案......

You could always create an extension getter to get the current value of an Observable.你总是可以创建一个扩展 getter来获取 Observable 的当前值。

To do this you would need to extend the Observable<T> interface in a global.d.ts typings declaration file.为此,您需要在global.d.ts声明文件中扩展Observable<T>接口。 Then implement the extension getter in a observable.extension.ts file and finally include both typings and extension file to your application.然后在observable.extension.ts文件中实现扩展 getter ,最后将类型和扩展文件包含到您的应用程序中。

You can refer to this StackOverflow Answer to know how to include the extensions into your Angular application.您可以参考此StackOverflow Answer以了解如何将扩展包含到您的 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...
  });

You could store the last emitted value separately from the Observable.您可以将最后发出的值与 Observable 分开存储。 Then read it when needed.然后在需要时阅读。

let lastValue: number;

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

The best way to do this is using Behaviur Subject , here is an example:最好的方法是使用Behaviur Subject ,这是一个例子:

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

A subscription can be created and after taking the first emitted item destroyed.可以创建订阅,并在销毁第一个发出的项目后。 Pipe is a function that uses an Observable as its input and returns another Observable as output, while not modifying the first observable. Pipe 是一个函数,它使用一个 Observable 作为输入并返回另一个 Observable 作为输出,同时不修改第一个 Observable。 Angular 8.1.0.角 8.1.0。 Packages: "rxjs": "6.5.3" , "rxjs-observable": "0.0.7"包: "rxjs": "6.5.3""rxjs-observable": "0.0.7"

  ngOnInit() {

    ...

    // If loading with previously saved value
    if (this.controlValue) {

      // Take says once you have 1, then close the subscription
      this.selectList.pipe(take(1)).subscribe(x => {
        let opt = x.find(y => y.value === this.controlValue);
        this.updateValue(opt);
      });

    }
  }

There are two ways you can achieve this.有两种方法可以实现这一点。

  1. BehaviorSubject has a method getValue() which you can get the value in a specific point of time. BehaviorSubject有一个方法getValue() ,您可以在特定时间点获取值。

  2. You can subscribe directly with the BehaviorSubject and you may pass the subscribed value to a class member, field or property.您可以直接使用 BehaviorSubject 进行订阅,并且可以将订阅的值传递给类成员、字段或属性。

I wouldn't recommend both approaches.我不会推荐这两种方法。

In the first approach, it's a convenient method you can get the value anytime, you may refer to this as the current snapshot at that point of time.第一种方法是一种方便的方法,可以随时获取值,可以将其称为该时间点的当前快照。 Problem with this is you can introduce race conditions in your code, you may invoke this method in many different places and in different timing which is hard to debug.问题是你可以在你的代码中引入竞争条件,你可能会在许多不同的地方和不同的时间调用这个方法,这很难调试。

The second approach is what most developers employ when they want a raw value upon subscription, you can track the subscription and when do you exactly unsubscribe to avoid further memory leak, you may use this if you're really desperate to bind it to a variable and there's no other ways to interface it.第二种方法是大多数开发人员在订阅时需要原始值时采用的方法,您可以跟踪订阅以及何时完全取消订阅以避免进一步的内存泄漏,如果您真的很想将其绑定到变量,则可以使用此方法并且没有其他方法可以连接它。

I would recommend, looking again at your use cases, where do you use it?我建议,再看看你的用例,你在哪里使用它? For example you want to determine if the user is logged in or not when you call any API, you can combine it other observables:例如你想在调用任何 API 时确定用户是否登录,你可以结合其他 observables:

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

With this, you may use it directly to the UI by piping data$ | async有了这个,你可以通过管道data$ | async直接在 UI 中使用它data$ | async data$ | async in case of angular.在角度的情况下data$ | async

Another approach, If you want / can to use async await (has to be inside of an async functions) you can do this with modern Rxjs:另一种方法,如果你想/可以使用异步等待(必须在异步函数内部),你可以使用现代 Rxjs 来做到这一点:

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

 }

 

This will emit a value "Right away" because of withLatestFrom , and then will resolve the promise.由于withLatestFrom ,这将发出一个“立即”值,然后将解析 promise。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM