我有一个使用observable异步工作的方法。我想知道使这个方法 async 的最佳方法是什么,这样我就可以等待,并在observable完成后做一些工作。
我的第一次尝试是在observable上使用 await 。
public async Task DoWorkAsync()
{
var observable = Observable.Create<int>(o =>
{
Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine("OnNext");
o.OnNext(1);
o.OnError(new Exception("exception in observable logic"));
//o.OnCompleted();
});
return Disposable.Empty;
});
//observable = observable.Publish().RefCount();
observable.Subscribe(i => Console.WriteLine(i));
Console.WriteLine("Awaiting...");
await observable;
Console.WriteLine("After Awaiting...");
}
根据情况,我对该方法有不同的问题(+/-表示这部分代码未注释/注释):
+ OnNext + OnCompleted -OnError -RefCount: OnNext 被调用2次(可观察的订阅次数为2次)。这是我想避免的。
+ OnNext + OnCompleted -OnError + RefCount:当我使用 RefCount()方法时,代码可以正常工作。
-OnNext + OnCompleted -OnError + RefCount:“Sequence is no element”当我的observable没有引发 OnNext 时抛出异常。
+ OnNext -OnCompleted + OnError -RefCount: OnNext 被调用了2次。提出例外。
+ OnNext -OnCompleted + OnError + RefCount:显示1后挂起(可能是因为它想要返回到等待的线程)。我们可以使用SubscribeOn(ThreadPoolScheduler.Instance)
无论如何,当observable为空(没有 OnNext 上升)时,即使没有调用 OnError ,我们也会得到异常,并且我们在可观察逻辑中没有任何异常。这就是为什么等待观察不是好的解决方案。
这就是我尝试使用TaskCompletionSource
的另一种解决方案的原因public async Task DoWorkAsync()
{
var observable = Observable.Create<int>(o =>
{
Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine("OnNext");
o.OnNext(1);
o.OnError(new Exception("exception in observable logic"));
//o.OnCompleted();
});
return Disposable.Empty;
});
var tcs = new TaskCompletionSource<bool>();
observable.Subscribe(i => Console.WriteLine(i),
e =>
{
//tcs.TrySetException(e);
tcs.SetResult(false);
},
() => tcs.TrySetResult(true));
Console.WriteLine("Awaiting...");
await tcs.Task;
Console.WriteLine("After Awaiting...");
}
它适用于所有场景,如果调用 OnError ,我们可以使用tcs.SetResult(false)并且在外部方法中没有关于异常细节的信息,或者我们可以使用tcs .TrySetException(e)并且能够在外部方法中捕获异常。
如果有更好/更清洁的解决方案,或者我的第二个解决方案是要走的路,你能建议我吗?
修改
所以我想知道是否有比我的第二个解决方案更好的解决方案:
答案 0 :(得分:2)
修改强>
如果您删除订阅,则可以执行以下操作:
await observable.Do(i => Console.WriteLine(i)).LastOrDefaultAsync();
至于你的任意要求......对于冷观察者没有多个订阅是有道理的;所以你发布它。拒绝使用.Publish().Refcount()
没有意义。我不明白为什么你拒绝解决问题的解决方案。
那里有很多,但我认为这是你的主要问题:
无论如何,如果observable为空(没有OnNext上升),我们得到 异常,即使没有调用OnError,我们也没有 可观察逻辑中的异常。这就是等待观察的原因 不好的解决方案。
await observable
与await observable.LastAsync()
相同。因此,如果没有元素,则会出现异常。想象一下,将该陈述更改为int result = await observable;
如果没有元素,result
的价值应该是多少?
如果您将await observable;
更改为await observable.LastOrDefaultAsync();
,一切都应该顺利进行。
是的,您应该使用.Publish().Refcount()
答案 1 :(得分:0)
我显然更喜欢第二种解决方案,因为它只订阅了一次。
但出于好奇:编写这样的方法的目的是什么? 如果允许可配置的副作用,这将是等效的:
public async Task DoWorkAsync()
{
Action<int> onNext = Console.WriteLine;
await Task.Delay(1000);
onNext(1);
throw new Exception("exception in DoWork logic"); // ... or don't
}
答案 2 :(得分:0)
您可以使用ToTask扩展方法:
await observable.ToTask();