此示例控制台应用程序有2个可观察对象。第一个将数字从1推送到100.这个observable由AsyncClass
订阅,class AsyncClass
{
private readonly IConnectableObservable<int> _source;
private readonly IDisposable _sourceDisposeObj;
public IObservable<string> _asyncOpObservable;
public AsyncClass(IConnectableObservable<int> source)
{
_source = source;
_sourceDisposeObj = _source.Subscribe(
ProcessArguments,
ExceptionHandler,
Completed
);
_source.Connect();
}
private void Completed()
{
Console.WriteLine("Completed");
Console.ReadKey();
}
private void ExceptionHandler(Exception exp)
{
throw exp;
}
private void ProcessArguments(int evtArgs)
{
Console.WriteLine("Argument being processed with value: " + evtArgs);
//_asyncOpObservable = LongRunningOperationAsync("hello").Publish();
// not going to work either since this creates a new observable for each value from main observer
}
// http://rxwiki.wikidot.com/101samples
public IObservable<string> LongRunningOperationAsync(string param)
{
// should not be creating an observable here, rather 'pushing' values?
return Observable.Create<string>(
o => Observable.ToAsync<string, string>(DoLongRunningOperation)(param).Subscribe(o)
);
}
private string DoLongRunningOperation(string arg)
{
return "Hello";
}
}
为每个数字运行一个长时间运行的进程。完成这个新的异步过程后,我希望能够“推送”到2个订阅者,这将使用这个新值做一些事情。
我的尝试在下面的源代码中得到了评论。
AsyncClass:
static void Main(string[] args)
{
var source = Observable
.Range(1, 100)
.Publish();
var asyncObj = new AsyncClass(source);
var _asyncTaskSource = asyncObj._asyncOpObservable;
var ui1 = new UI1(_asyncTaskSource);
var ui2 = new UI2(_asyncTaskSource);
}
主:
class UI1
{
private IConnectableObservable<string> _asyncTaskSource;
private IDisposable _taskSourceDisposable;
public UI1(IConnectableObservable<string> asyncTaskSource)
{
_asyncTaskSource = asyncTaskSource;
_asyncTaskSource.Connect();
_taskSourceDisposable = _asyncTaskSource.Subscribe(RefreshUI, HandleException, Completed);
}
private void Completed()
{
Console.WriteLine("UI1: Stream completed");
}
private void HandleException(Exception obj)
{
Console.WriteLine("Exception! "+obj.Message);
}
private void RefreshUI(string obj)
{
Console.WriteLine("UI1: UI refreshing with value "+obj);
}
}
UI1(和UI2,它们基本相同):
{{1}}
这是我与Rx的第一个项目,所以让我知道我是否应该以不同的方式思考。任何帮助将非常感谢!
答案 0 :(得分:1)
我打算让你知道你应该以不同的方式思考... :)除了轻浮之外,这看起来像是面向对象和功能反应风格之间的错误碰撞。
目前还不清楚数据流的时间要求和结果缓存的要求是什么 - 使用Publish
和IConnectableObservable
有点困惑。我猜你想要避免2个下游订阅导致处理重复的值?我的一些答案就是基于这个前提。使用Publish()
可以通过允许多个订阅者共享单个源的订阅来实现此目的。
Idiomatic Rx希望您尝试保持功能风格。为此,您希望将长时间运行的工作作为一项功能提供。所以,让我们说,不要试图将AsyncClass逻辑直接连接到Rx链中作为一个类,你可以将它作为一个函数来表示,如下所示:
async Task<int> ProcessArgument(int argument)
{
// perform your lengthy calculation - maybe in an OO style,
// maybe creating class instances and invoking methods etc.
await Task.Delay(TimeSpan.FromSeconds(1));
return argument + 1;
}
现在,您可以构建一个完整的Rx可观察链来调用此函数,并且通过使用Publish().RefCount()
,您可以避免多个订阅者导致重复工作。注意这也是如何区分问题的 - 处理值的代码更简单,因为重用是在别处处理的。
var query = source.SelectMany(x => ProcessArgument(x).ToObservable())
.Publish().RefCount();
通过为订阅者创建单个链,只有在订阅时才需要开始工作。我已使用Publish().RefCount()
- 但如果您想确保第二个及后续订阅者不会错过任何值,您可以使用Replay
(简单)或使用Publish()
然后{{ 1}} - 但是你需要在个人订阅者代码之外的Connect
逻辑,因为你只需要在所有订阅者订阅时调用它一次。