我有以下代码:
IObservable<Data> _source;
...
_source.Subscribe(StoreToDatabase);
private async Task StoreToDatabase(Data data) {
await dbstuff(data);
}
但是,这不会编译。有没有办法如何异步观察数据?我试过async void
,它有效,但我觉得给定解决方案是不可行的。
我还检查了Reactive Extensions Subscribe calling await,但它没有回答我的问题(我不关心SelectMany
结果。)
答案 0 :(得分:9)
您不必关心SelectMany
结果。答案仍然是相同的......虽然你需要你的任务有一个返回类型(即Task<T>
,而不是Task
)。
Unit
基本上等同于void
,因此您可以使用它:
_source.SelectMany(StoreToDatabase).Subscribe();
private async Task<Unit> StoreToDatabase(Data data)
{
await dbstuff(data);
return Unit.Default;
}
此SelectMany
重载接受Func<TSource, Task<TResult>
,表示在任务完成之前,结果序列将无法完成。
答案 1 :(得分:3)
迟到的答案,但我认为以下扩展方法正确地封装了Charles Mager在his answer中提出的内容:
public static IDisposable SubscribeAsync<T>(this IObservable<T> source,
Func<Task> asyncAction, Action<Exception> handler = null)
{
Func<T,Task<Unit>> wrapped = async t =>
{
await asyncAction();
return Unit.Default;
};
if(handler == null)
return source.SelectMany(wrapped).Subscribe(_ => { });
else
return source.SelectMany(wrapped).Subscribe(_ => { }, handler);
}
public static IDisposable SubscribeAsync<T>(this IObservable<T> source,
Func<T,Task> asyncAction, Action<Exception> handler = null)
{
Func<T, Task<Unit>> wrapped = async t =>
{
await asyncAction(t);
return Unit.Default;
};
if(handler == null)
return source.SelectMany(wrapped).Subscribe(_ => { });
else
return source.SelectMany(wrapped).Subscribe(_ => { }, handler);
}
答案 2 :(得分:0)
我一直在使用TPL DataFlow来控制背压,并用它来解决这个问题。
关键部分是ITargetBlock<TInput>.AsObserver()
- source。
// Set a block to handle each element
ITargetBlock<long> targetBlock = new ActionBlock<long>(async p =>
{
Console.WriteLine($"Received {p}");
await Task.Delay(1000);
Console.WriteLine($"Finished handling {p}");
},
new ExecutionDataflowBlockOptions { BoundedCapacity = 1 });
// Generate an item each second for 10 seconds
var sequence = Observable.Interval(TimeSpan.FromSeconds(1)).Take(10);
// Subscribe with an observer created from the target block.
sequence.Subscribe(targetBlock.AsObserver());
// Await completion of the block
await targetBlock.Completion;
这里的重要部分是ActionBlock的有界容量设置为1.这可以防止块一次接收多个项目,will block OnNext如果项目已经被处理了!
我最大的惊喜是,在您的订阅中调用Task.Wait
和Task.Result
是安全的。显然,如果你调用ObserverOnDispatcher()
或类似的话,你可能会遇到死锁。小心!
答案 3 :(得分:0)
因此,您要运行存储数据过程,可能要运行其他过程,并异步等待完成或部分结果。如何显示此处显示的构造函数:
IObservable<Int32> NotifyOfStoringProgress =
Observable.Create(
(Func<IObserver<Int32>, Task>)
(async (ObserverToFeed) =>
{
ObserverToFeed.OnNext(-1);
Task StoreToDataBase = Task.Run(()=> {;});
ObserverToFeed.OnNext(0);
;;
await StoreToDataBase;
ObserverToFeed.OnNext(1);
;;
}));
NotifyOfStoringProgress.Subscribe(onNext: Notification => {;});