RxJS可观察:订阅丢失了? [英] RxJS Observable: Subscription lost?

查看:90
本文介绍了RxJS可观察:订阅丢失了?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下两个可观察的映射之间有什么区别?

What is the difference between the following two observable mappings?

(如果以下代码中的某些内容对您来说很奇怪:它源于边做边学的业余爱好项目;我仍然在学习RxJS)

(if something in the following code appears strange to you: it stems from a learning-by-doing hobby project; I still learn RxJS)

我有一个带有getter和构造函数的组件.两者都从应用程序的ngrx存储中读取信息,并提取一个字符串(name).

I have a component with a getter and a constructor. Both read information from the app's ngrx store and extract a string (name).

getter和构造函数之间的唯一区别: getter用于HTML,它返回的可观察对象通过async管道发送,而构造函数中的可观察映射已完成通过使用subscribe进行订阅.我希望随着name的新值可用,它们都会触发.

The only difference between the getter and the constructor: the getter is used in the HTML and the observable it returns is sent through an async pipe, whereas the observable mapping in the constructor is finished by a subscription using subscribe. I expect both of them to fire as often as a new value for name becomes available.

但是,只有getter可以那样工作,并在HTML中提供async管道,并在管道中将其与新的name值一起使用(每次更改名称都会调用console.log('A')). subscribe订阅的回调仅被调用一次:console.log('B')console.log('B!')都被恰好调用一次,并且永远不会被调用.

But instead only the getter works that way and provides the async pipe in the HTML where it is used with new values of name (console.log('A') is called for every name change). The subscribe subscription's callback is called only once: console.log('B') and console.log('B!') are both called exactly once and never again.

如何解释这种行为差异?

How can this difference in behavior be explained?

我的组件的片段:

// getter works exactly as expected:
get name$(): Observable<string> {
  console.log('getter called')
  return this.store
    .select(this.tableName, 'columns')
    .do(_ => console.log('DO (A)', _))
    .filter(_ => !!_)
    .map(_ => _.find(_ => _.name === this.initialName))
    .filter(_ => !!_)
    .map(_ => {
      console.log('A', _.name)
      return _.name
    })
}

// code in constructor seems to lose the subscription after the subscription's first call:
constructor(
  @Inject(TablesStoreInjectionToken) readonly store: Store<TablesState>
) {
  setTimeout(() => {
    this.store
      .select(this.tableName, 'columns')
      .do(_ => console.log('DO (B)', _))
      .filter(_ => !!_)
      .map(_ => _.find(_ => _.name === this.initialName))
      .filter(_ => !!_)
      .map(_ => {
        console.log('B', _.name)
        return _.name
      })
      .subscribe(_ => console.log('B!', _))
  })
}

其他信息:如果添加ngOnInit,则此生命周期挂钩在整个测试期间仅被调用一次.如果我将订阅从构造函数移到ngOnInit生命周期挂钩,那么它的工作效果不会比在构造函数内部更好.完全相同(意外)的行为. ngAfterViewInit和其他生命周期挂钩也是如此.

Additional information: If I add ngOnInit, this life cycle hook is called exactly once during the whole test. If I move the subscription from the constructor to the ngOnInit life cycle hook, it does not work any better than from within the constructor. Exactly the same (unexpected) behavior. The same applies to ngAfterViewInit and further life cycle hooks.

名称更改的输出'some-name' -> 'some-other-name' -> 'some-third-name' -> 'some-fourth-name' -> 'some-fifth-name':

如Pace在其评论中建议的[UPDATE],我添加了getter调用日志

[UPDATE] as suggested by Pace in their comment, I added getter call logs

[更新] do已按照Pace的建议

[UPDATE] dos added as suggested by Pace

getter called
DO (A) (3) [{…}, {…}, {…}]
A some-name
DO (B) (3) [{…}, {…}, {…}]
B some-name
B! some-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-other-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-third-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-fourth-name
getter called
DO (A) (3) [{…}, {…}, {…}]
A some-fifth-name

do s在do s中打印的输出的示例内容:

Example content of the output printed by the console.logs in the dos:

[
  {
    "name": "some-name"
  },
  {
    "name": "some-other-name"
  },
  {
    "name": "some-third-name"
  }
]

好像subscribe订阅在其第一次调用后丢失了.但是为什么呢?

Seems as if the subscribe subscription gets lost after its first call. But why?

推荐答案

ngrx.select()返回的Observable仅在存储中的数据发生更改时才会触发.

The Observable returned from ngrx.select() will only fire when the data in the store has changed.

如果要在initialName更改时触发Observable,则建议将initialName转换为RXJS Subject并使用combineLatest:

If you want the Observable to fire when initialName changes, then I would recommend converting initialName into an RXJS Subject and using combineLatest:

initialNameSubject = new BehaviorSubject<string>('some-name');

constructor(
  @Inject(TablesStoreInjectionToken) readonly store: Store<TablesState>
) {
  setTimeout(() => {
    this.store
      .select(this.tableName, 'columns')
      .combineLatest(this.initialNameSubject)
      .map(([items, initialName]) => items.find(_ => _.name === initialName))
      .filter(_ => !!_)
      .map(_ => {
        console.log('B', _.name)
        return _.name
      })
      .subscribe(_ => console.log('B!', _))
  })
}

这篇关于RxJS可观察:订阅丢失了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆