如何在Rx中使用异步方法进行订阅?

时间:2016-05-24 11:22:42

标签: c# async-await system.reactive

我有以下代码:

IObservable<Data> _source;

...

_source.Subscribe(StoreToDatabase);

private async Task StoreToDatabase(Data data) {
    await dbstuff(data);
}

但是,这不会编译。有没有办法如何异步观察数据?我试过async void,它有效,但我觉得给定解决方案是不可行的。

我还检查了Reactive Extensions Subscribe calling await,但它没有回答我的问题(我不关心SelectMany结果。)

4 个答案:

答案 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.WaitTask.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 => {;});