问题描述
以下两个可观察的映射之间有什么区别?
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] do
s 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.log
s in the do
s:
[
{
"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可观察:订阅丢失了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!