简体   繁体   English

如何在第一次调用时初始化由 BehaviorSubject 支持的可观察对象?

[英]How to initialize an observable backed by a BehaviorSubject on first call?

I have an Observable that is backed by a private BehaviorSubject.我有一个由私有 BehaviorSubject 支持的 Observable。 On the first emit, I would like to initialize the BehaviorSubject from an async call, but I can't come up with a great pattern to do so.在第一次发出时,我想通过异步调用初始化 BehaviorSubject,但我想不出一个很好的模式来这样做。 As far as I can tell, BehaviorSubjects can't be initialized from an async function.据我所知,无法从异步 function 初始化 BehaviorSubjects。

What I have so far is:到目前为止我所拥有的是:

protected _monkeyNames = new BehaviorSubject<Set<string>>(null);

MonkeyNames$: Observable<Set<string>> = this._monkeyNames.pipe(
  switchMap(async (nodes) => nodes ?? (await this.getMonkeyNames()))
);

protected async getMonkeyNames(): Promise<Set<string>> {
  const names = new Set(await this.stateService.getMonkeyNames());
  return names;
}

But this won't set the BehaviorSubject, it will only be set when I call setMonkeyNames later to save a new value.但这不会设置 BehaviorSubject,它只会在我稍后调用setMonkeyNames以保存新值时设置。 If I do call .next() inside of getMonkeyNames , the Observable will emit again, which could result in an eternal loop if names is null .如果我确实在getMonkeyNames内部调用.next() ,Observable 将再次发射,如果namesnull ,这可能会导致一个永恒的循环。

It might be my own ignorance when it comes to rxjs, but does anyone have a pattern they use for this?关于 rxjs,这可能是我自己的无知,但是有人有他们使用的模式吗?

Edit: I should mention that this is a service, and I won't have access to ngOnInit()编辑:我应该提到这是一项服务,我无法访问ngOnInit()

BehaviorSubject is useful only when there is a initial value. BehaviorSubject只有在有初始值时才有用。 Since you don't have any, ReplaySubject or Subject can be used.由于您没有,因此可以使用ReplaySubjectSubject Also initializing BehaviorSubject with null, when type clearly restricts to Set<...>, is just an error.还使用 null 初始化 BehaviorSubject,当类型明确限制为 Set<...> 时,这只是一个错误。

If your service returns promise of some value, convert it to Observable with from and process in operator chain.如果您的服务返回某个值的 promise,请使用from和 process in operator chain 将其转换为 Observable。

monkeyNames$: Observable<Set<string>> = from(this.stateService.getMonkeyNames())
.pipe(
map(names=> new Set(names))
)

What you can do is initialize the BehaviorSubject with an empty Set , then do the api call or async call in the constructor of the service that would set the initial value for the first time.您可以做的是用空Set初始化BehaviorSubject ,然后在首次设置初始值的服务构造函数中执行 api 调用或异步调用。

protected _monkeyNames = new BehaviorSubject<Set<string>>(new Set());

MonkeyNames$: Observable<Set<string>> = this._monkeyNames.asObservable();

protected async getMonkeyNames(): Promise<Set<string>> {
  const names = new Set(await this.stateService.getMonkeyNames());
  this._monkeyNames.next(names);
  return names;
}

constructor() {
  this.getMonkeyNames();
}

Example HTML Template:示例 HTML 模板:

<div *ngIf="MonkeyNames$ | async as names">
  <div *ngFor="let name of names">{{ name }}</div>
</div>

Here is a working Stackblitz example (with some extra debugging; see console).这是一个有效的Stackblitz 示例(带有一些额外的调试;请参阅控制台)。

Instead of attempting to initialize the subject, maybe you can declare your MonkeyNames$ from two different sources using merge :与其尝试初始化主题,也许您可以使用merge从两个不同的来源声明您的MonkeyNames$

protected _monkeyNames = new Subject<Set<string>>();

MonkeyNames$: Observable<Set<string>> = merge(
    from(this.getMonkeyNames()),
    this._monkeyNames
).pipe(
    shareReplay(1)
);

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

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