为什么 Observable.range 在 GUI 线程上 运行 时冻结?
Why does Observable.range freeze when run on the GUI thread?
module StackTenButtons.Try2
open System
open System.Windows
open System.Windows.Controls
open System.Reactive.Linq
open System.Reactive.Disposables
open FSharp.Control.Reactive
let control c l =
Observable.Create (fun (sub : IObserver<_>) ->
let c = c()
let d = new CompositeDisposable()
List.iter (fun x -> d.Add(x c)) l
sub.OnNext(c)
d :> IDisposable
)
let do' f c = f c; Disposable.Empty
let prop s v c = Observable.subscribe (s c) v
let w =
control Window [
prop (fun t v -> t.Content <- v) <| control StackPanel [
do' (fun pan ->
Observable.range 0 10
|> Observable.subscribe (fun x -> pan.Children.Add(Button(Content=sprintf "Button %i" x)) |> ignore)
|> ignore
)
]
]
[<STAThread>]
[<EntryPoint>]
let main _ = w.Subscribe (Application().Run >> ignore); 0
我正在尝试为反应式 UI 制作一个小型概念证明库,并且在尝试编写向父级添加多个控件的函数时遇到了这个问题。标准 属性 设置在它们是单例时有效,但在使用像 Observable.range
这样的迭代器函数时无效。
有可能使这项工作吗?
由于 F# 需要手动将一些内容添加到项目文件中,以便可以使用 WPF,here is 用于此的 repo。
Range
的默认调度程序是 Scheduler.CurrentThread
。
CurrentThread
和 Immediate
的行为有时会导致永久性蹦床或死锁,尤其是在尝试与 Observable.Create
或类似的非预定冷可观察对象同步使用时。
他们锁定的确切原因很难描述,但与发现的行为相似 and 。
Observable.Create (fun (sub : IObserver<_>) ->
sub.OnNext(1)
sub.OnNext(2)
sub.OnNext(3)
d :> IDisposable //<-- this dispose should cancel all `OnNext`
)
永远不能调用上面的处理,以防止物品被发射 - 直到 在 物品被发射之前。如果您手动构造一个可观察对象,请尝试使其接受调度程序参数。
DispatcherScheduler
回到 Rx.NET 核心可能还有一段路要走。
这是一个最小 DispatcherScheduler
:
的实现
type DispatcherScheduler =
static member Instance = {
new IScheduler with
member _.Now = DateTimeOffset.Now
member _.Schedule<'S>(state: 'S, action: Func<IScheduler, 'S, IDisposable>) : IDisposable =
let op = Application.Current.Dispatcher.InvokeAsync(fun () -> action.Invoke(DispatcherScheduler.Instance, state))
Disposable.Create(fun () -> op.Abort() |> ignore)
member _.Schedule<'S>(state: 'S, dueTime: TimeSpan, action: Func<IScheduler, 'S, IDisposable>) : IDisposable = failwith "Not Impl"
member _.Schedule<'S>(state: 'S, dueTime: DateTimeOffset, action: Func<IScheduler, 'S, IDisposable>) : IDisposable = failwith "Not Impl"
}
module StackTenButtons.Try2
open System
open System.Windows
open System.Windows.Controls
open System.Reactive.Linq
open System.Reactive.Disposables
open FSharp.Control.Reactive
let control c l =
Observable.Create (fun (sub : IObserver<_>) ->
let c = c()
let d = new CompositeDisposable()
List.iter (fun x -> d.Add(x c)) l
sub.OnNext(c)
d :> IDisposable
)
let do' f c = f c; Disposable.Empty
let prop s v c = Observable.subscribe (s c) v
let w =
control Window [
prop (fun t v -> t.Content <- v) <| control StackPanel [
do' (fun pan ->
Observable.range 0 10
|> Observable.subscribe (fun x -> pan.Children.Add(Button(Content=sprintf "Button %i" x)) |> ignore)
|> ignore
)
]
]
[<STAThread>]
[<EntryPoint>]
let main _ = w.Subscribe (Application().Run >> ignore); 0
我正在尝试为反应式 UI 制作一个小型概念证明库,并且在尝试编写向父级添加多个控件的函数时遇到了这个问题。标准 属性 设置在它们是单例时有效,但在使用像 Observable.range
这样的迭代器函数时无效。
有可能使这项工作吗?
由于 F# 需要手动将一些内容添加到项目文件中,以便可以使用 WPF,here is 用于此的 repo。
Range
的默认调度程序是 Scheduler.CurrentThread
。
CurrentThread
和 Immediate
的行为有时会导致永久性蹦床或死锁,尤其是在尝试与 Observable.Create
或类似的非预定冷可观察对象同步使用时。
他们锁定的确切原因很难描述,但与发现的行为相似
Observable.Create (fun (sub : IObserver<_>) ->
sub.OnNext(1)
sub.OnNext(2)
sub.OnNext(3)
d :> IDisposable //<-- this dispose should cancel all `OnNext`
)
永远不能调用上面的处理,以防止物品被发射 - 直到 在 物品被发射之前。如果您手动构造一个可观察对象,请尝试使其接受调度程序参数。
DispatcherScheduler
回到 Rx.NET 核心可能还有一段路要走。
这是一个最小 DispatcherScheduler
:
type DispatcherScheduler =
static member Instance = {
new IScheduler with
member _.Now = DateTimeOffset.Now
member _.Schedule<'S>(state: 'S, action: Func<IScheduler, 'S, IDisposable>) : IDisposable =
let op = Application.Current.Dispatcher.InvokeAsync(fun () -> action.Invoke(DispatcherScheduler.Instance, state))
Disposable.Create(fun () -> op.Abort() |> ignore)
member _.Schedule<'S>(state: 'S, dueTime: TimeSpan, action: Func<IScheduler, 'S, IDisposable>) : IDisposable = failwith "Not Impl"
member _.Schedule<'S>(state: 'S, dueTime: DateTimeOffset, action: Func<IScheduler, 'S, IDisposable>) : IDisposable = failwith "Not Impl"
}