我试图理解如何从FSharp.Control.Reactive观察计算工作,所以我重新实现了Observable.take组合
这是我最初的尝试:
let myTake (n : int) (source : IObservable<'a>) : IObservable<'a> =
let rec go counter : IObservable<'a> =
observe {
let! v = source
if counter < n
then yield v
yield! go (counter + 1)
}
go 0
但是,当我运行以下测试时:
use subcription =
subj
|> myTake 2
|> Observable.subscribe (printfn "next:%d")
subj.OnNext 1
subj.OnNext 2
subj.OnNext 3
waitForKey "my take"
我得到了输出:
next:1
next:2
next:2
next:3
next:3
next:3
我该如何解决这个问题?
我还尝试在使用observable.ofSeq创建的observable上运行myTake,它甚至更糟糕,即它只是重复生成输入序列几次。我假设它与ofSeq返回冷可观察但不完全理解行为的事实有关。
我怎样才能使它与冷可观察量一起使用?
谢谢!
答案 0 :(得分:3)
我对model(params) {
return this.store.queryRecord('name', params);
}
计算构建器不太熟悉,但快速查看源代码显示observable
操作(位于Bind
后面)是{{ 3}}。 let!
操作将启动每次事件发生的其余工作流程,因此您看到的行为是预期的行为。
来自implemented using the Rx SelectMany operation的图片:
this article illustrates the behaviour well
我不确定使用SelectMany
计算构建器实现Observable.take
的好方法 - 坦率地说,我一直认为observable不是特别适合F#计算表达式,因为计算表达式带来的通常直觉根本不适用于基于推送的可观察量。
我认为如果你只是编写内置操作就可以解决observable的问题,但是当你需要实现自己的自定义原语时它们并不是特别好 - 而且大多数时候,我只是使用F#代理实现逻辑并将其包装并可观察。
答案 1 :(得分:2)
我同意托马斯的观点,即建造者的本地操作似乎不支持你想要的东西。但是,我的总体结论与他不同 - 我不明白为什么可观察量对于计算表达式而言比计算表达式更有问题。
首先,考虑如何在序列表达式中编写take
。在那里,let!
不受支持;你需要使用for
循环。但是你的代码类似于:
let rec go counter = seq {
for v in source do
if counter < n then
yield v
yield! go (counter + 1)
}
从一个序列中取出就像从一个可观察者那里获取一样:计数器有效地告诉你循环通过笛卡尔积的次数,这不是你想要的。一种解决方案是使计数器变为可变并将其移动到表达式中(然后不再需要递归):
seq {
let mutable counter = 0
for v in source do
if counter < n then
yield v
counter <- counter + 1
}
这适用于序列,但是对于可观察对象呢? observe
也支持for
循环,但仅用于循环序列而不是可观察的循环。但是,我们可以通过重载构建器的For
方法来删除此限制:
type FSharp.Control.Reactive.Builders.ObservableBuilder with
member __.For(o, f) =
FSharp.Control.Reactive.Observable.bind f o
现在我们可以像对序列一样轻松地编写take
:
let myTake (n : int) (source : IObservable<'a>) : IObservable<'a> =
observe {
let mutable counter = 0
for v in source do
if counter < n then
yield v
counter <- counter + 1
}
您可以验证它是否有效。