我遇到了ReactiveCommand处理ObserveOn和SubscribeOn的问题。
我有一个返回可观察字符串序列的API,它看起来像这样:
public IObservable<string> GetDocumentObservable(int numParagraphs, int latency)
{
return Observable.Create<string>(obs =>
{
for (int i = 0; i < numParagraphs; i++)
{
Console.WriteLine("Service On thread {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
obs.OnNext("Some String");
}
obs.OnCompleted();
return Disposable.Empty;
});
}
我正在使用ReactiveCommand.CreateAsyncObservable来调用它,使用SubscribeOn(RxApp.TaskpoolScheduler)(以确保Thread.Sleep不会在UI线程上发生),并使用ObserveOn(RxApp.MainThreadScheduler)来绘制我的字符串UI线程。
不幸的是,这一切都是同步执行的(在同一个线程上),我不知道为什么。这是VM代码看起来像:
DownloadDocument = ReactiveCommand
.CreateAsyncObservable(_ =>
{
Console.WriteLine("ViewModel Invoking On thread {0}", Thread.CurrentThread.ManagedThreadId);
return _documentService.GetDocumentObservable(NumParagraphs, 0);
});
DownloadDocument
.SubscribeOn(RxApp.TaskpoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(p =>
{
Console.WriteLine("ViewModel OnNext thread {0}", Thread.CurrentThread.ManagedThreadId);
Document.Add(p);
},
x => { },
() => { Console.WriteLine("ViewModel OnComplete thread {0}", Thread.CurrentThread.ManagedThreadId); });
Eveything在同一个线程上执行,并阻止UI线程。 如果我以“老式方式”调用它,一切都按预期工作(如下所示):
Something = ReactiveCommand.Create();
Something.Subscribe(x =>
{
_documentService.GetDocumentObservable(NumParagraphs, 0)
.SubscribeOn(RxApp.TaskpoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(p =>
{
Console.WriteLine("ViewModel OnNext thread {0}", Thread.CurrentThread.ManagedThreadId);
Document.Add(p);
},
ex => { },
() => { Console.WriteLine("ViewModel OnComplete thread {0}", Thread.CurrentThread.ManagedThreadId); });
});
此处没有阻止的帖子。
在使用带有Observable apis的ReactiveCommands时,是否有一些我缺少的东西?
答案 0 :(得分:2)
当调用命令时,ReactiveCommand本身会订阅您的源代码,但不会获取您的SubscribeOn。解决这个问题的最简单方法是将代码包装在Task.Run中:
return Observable.Create<string>(obs =>
{
bool stopEarly;
Task.Run(() => {
for (int i = 0; i < numParagraphs; i++)
{
Console.WriteLine("Service On thread {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
obs.OnNext("Some String");
if (stopEarly) return;
}
obs.OnCompleted();
});
return Disposable.Create(() => stopEarly = true);
});
答案 1 :(得分:0)
您的GetDocumentObservable
方法没有提供Rx在另一个线程上运行代码,所以在订阅期间它会立即运行所有值并在完成对.Subscribe(...)
的调用之前完成信号。< / p>
在编写像这样的代码时要注意的一些关键事项是使用return Disposable.Empty;
和Thread.Sleep(...);
。它们应该是你的危险信号。
相反,您应该首先尝试使用其他内置方法来实现您的方法,而只是转到Create
&#34;滚动自己的&#34;什么时候必须。
幸运的是,这是一款非常强大的内置运算符,可以完美满足您的需求 - Generate
。此方法对于生成包含&#34; sleep&#34;
这就是它的样子:
public IObservable<string> GetDocumentObservable(int numParagraphs, int latency)
{
return
Observable
.Generate(
0,
i => i < numParagraphs,
i => i + 1,
i => "Some String",
i => i == 0
? TimeSpan.Zero
: TimeSpan.FromSeconds(1.0))
.Do(x => Console.WriteLine(
"Service On thread {0}",
Thread.CurrentThread.ManagedThreadId));
}
这将完成您的方法所做的一切,并且应该按照您的意愿行事。