我是反应式编程的新手,遇到了一个问题......
我的代码看起来像这样:
IsBusy = true;
service.BeginGetClients(param, c =>
{
var r = service.EndGetClients(c);
if(!CheckResult(r))
{
service.BeginGetCachedClients(param, c2 =>
{
var r2 = service.EndGetCachedClients(c2);
if(CheckResult(r2))
ShowMessage("clients valid");
else
ShowMessage("clients not valid");
UpdateClients(r2);
service.BeginUpdateClients(r2, c3 =>
{
var b = service.EndUpdateClients(c3);
if(b)
ShowMessage("clients updated");
else
ShowMessage("clients not updated");
IsBusy = false;
}, null);
}, null);
}
else
{
ShowMessage("error on get clients");
IsBusy = false;
}
}, null);
如何更改为流利的Rx?我从这段代码开始:
var invokeClients = Observable.FromAsyncPattern<Param, List<Client>>(service.BeginGetClients, service.EndGetClients);
var invokeCachedClients = Observable.FromAsyncPattern<Param, List<Client>>(service.BeginGetCachedClients, service.EndGetCachedClients);
invokeClients(param)
.Subscribe(r =>
{
if(!CheckResult(r))
{
invokeCachedClients(param)
.Subscribe(r2 =>
{
// TODO: next op
});
}
});
改进此代码的任何建议?也许另一个解决方 我不喜欢这个级联代码...
谢谢!
答案 0 :(得分:1)
invokeClients(param)
.Where(x => !CheckResult(x))
.Select(invokeCachedClients)
.Do(_ => IsBusy = true)
.Merge()
.Do(_ => IsBusy = false)
.Subscribe(x => Console.WriteLine("Do something here"));
确保拥有该订阅,否则它将无法工作(它就像不是Foreach的LINQ查询)
答案 1 :(得分:1)
根据上面的答案
invokeClients(param)
.Where(x => !CheckResult(x))
.SelectMany(invokeCachedClients)
.Subscribe(x => Console.WriteLine("Do something here"));
也可以写成
var query = from client in invokeClients(param)
where !CheckResult(client)
from cache in invokeCachedClients(client)
select cache;
然后你可以将忙标志包裹在资源
中Func<IDisposable> busyResource =
() =>
{
IsBusy = true;
return Disposable.Create(() => IsBusy = false);
};
现在您可以使用“使用工厂”来完成它。
Observable.Using(busyResource, _=>query)
.Subscribe(x=>Console.Write("Do something here");
你赞成使用Using方法而不是将IsBusy setter设置为OnError或OnCompleted的原因是因为如果订阅也被处理掉,它将会停止它。
我相信我们可能做得比这更好,但我发现很难理解你在做什么。老实说,我认为这实际上比Rx更适合TPL或数据工作流,即实际上是在链接工作延续而不是对一系列事件做出反应。
答案 2 :(得分:0)
对于这样的事情,你总是想要进去。翻译这个的最难的部分是异步调用之间的部分。如果你将一个权利的结果输入下一个,那将是一个直接的from x in async1() from y in async2(x) ...
。我看到两个函数,我将其分解为:
public IObserservable<string> UpdateCachedClients(object param)
{
var getCachedClientsAsync = Observable.FromAsyncPattern<...>(service.BeginGetCachedClients, service.EndGetCachedClients);
var updateClientsAsync = Observable.FromAsyncPattern<...>(service.BeginUpdateClients, service.EndUpdateClients);
return Observable.Create<string>(obs =>
{
return (from r2 in getCachedClientsAsync(param)
.Do(v =>
{ if (CheckResult(v))
obs.OnNext("clients valid");
else
obs.OnNext("clients not valid");
//huh? is this done twice
UpdateClients(v);
})
from b in updateClientsAsync(r2)
select (b ? "clients updated" : "clients not updated")
).Subscribe(obs);
});
}
public IObservable<string> UpdateAllClients(object param)
{
var getClientsAsync = Observable.FromAsyncPattern<...>(service.BeginGetClients, service.EndGetClients);
return from r in getClientsAsync(param)
select (CheckResult(r) ?
Observable.Return("error on get clients") :
UpdateCachedClients(param));
}
我在第一个函数中使用了额外的Observable.Create
层,因为它似乎是传递结果并继续下一次调用的最简单方法。一旦掌握了这两个功能,您就可以将它们称为:
IsBusy = true;
var disp = UpdateAllClients(param)
.Subscribe(ShowMessage,
ex => IsBusy = false,
() => IsBusy = false);
请注意,我在OnError和OnCompleted中都将IsBusy
设置为false,因为它们是IObservable的终止消息。
TPL似乎更适合像这样的异步方法,因为它们只生成一个值,但是在使用async / await的语言的下一个版本之前,你可能会得到与你的方法或我的方法类似的语法。您使用Tasks而不是IObservables。