ReactiveCommand未按预期处理可观察的调用

时间:2015-03-06 01:02:39

标签: c# system.reactive reactiveui

我遇到了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时,是否有一些我缺少的东西?

2 个答案:

答案 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));
}

这将完成您的方法所做的一切,并且应该按照您的意愿行事。