替代使用async()=>在Rx中最后

时间:2017-07-20 04:22:30

标签: c# asynchronous async-await system.reactive

我的理解是,与async void一起使用时,async () =>should be avoidedasync void只是伪装成Action

因此,应避免与async () =>异步使用the Rx.NET Finally operator,因为最终接受Action作为参数:

IObservable<T>.Finally(async () =>
{
    await SomeCleanUpCodeAsync();
};

但是,如果这是不好的做法,那么在我需要异步关闭网络coOnCompleted问题的情况下,如果我的observable是OnError或OnCompleted,那么最佳做法是什么?

4 个答案:

答案 0 :(得分:2)

  

我的理解是异常无效,应该避免async () =>伪装成async void

这是部分错误。 async () =>可以匹配Func<Task>(好)或Action(差)。好/坏的主要原因是async void调用中发生的异常会使进程崩溃,而async Task异常是可捕获的。

因此,我们只需编写一个AsyncFinally运算符,其中包含Func<Task>而不是Action Observable.Finally

public static class X
{
    public static IObservable<T> AsyncFinally<T>(this IObservable<T> source, Func<Task> action)
    {
        return source
            .Materialize()
            .SelectMany(async n =>
            {
                switch (n.Kind)
                {
                    case NotificationKind.OnCompleted:
                    case NotificationKind.OnError:
                        await action();
                        return n;
                    case NotificationKind.OnNext:
                        return n;
                    default:
                        throw new NotImplementedException();
                }
            })
            .Dematerialize()
        ;
    }
}

以下是使用示例:

try
{
    Observable.Interval(TimeSpan.FromMilliseconds(100))
        .Take(10)
        .AsyncFinally(async () =>
        {
            await Task.Delay(1000);
            throw new NotImplementedException();
        })
        .Subscribe(i => Console.WriteLine(i));
}
catch(Exception e)
{
    Console.WriteLine("Exception caught, no problem");
}

如果您为AsyncFinally换出Finally,则会导致流程崩溃。

答案 1 :(得分:1)

它在Rx中,因为它在其他地方;像瘟疫一样避免async void。除了本文中列出的问题之外,在同步运算符中使用异步代码“中断”Rx。

我会考虑使用OnErrorResumeNext()异步清理资源。 OnErrorResumeNext()让你指定一个在第一个之后运行的observable,无论它结束的原因是什么:

var myObservable = ...

myObservable
    .Subscribe( /* Business as usual */ );

Observable.OnErrorResumeNext(
        myObservable.Select(_ => Unit.Default),
        Observable.FromAsync(() => SomeCleanUpCodeAsync()))
    .Subscribe();

myObservable最好是ConnectableObservable(例如Publish()),以防止多次订阅。

答案 2 :(得分:1)

Finally的方法签名是

public static IObservable<TSource> Finally<TSource>(
    this IObservable<TSource> source,
    Action finallyAction
)

期望一个动作,而不是一个任务。

作为附录,如果要异步运行某些内容而不是异步void,请在方法内使用Task.Factory方法,以便明确意图。

答案 3 :(得分:1)

引自Intro to Rx

<块引用>

Finally 扩展方法接受 Action 作为参数。如果序列正常终止或错误终止,或者订阅被丢弃,则将调用此 Action

(强调)

此行为无法被接受 Finally 参数的 Func<Task> 运算符复制,因为 IObservable<T> 接口是如何定义的。取消订阅可观察序列是通过调用 IDisposable 订阅的 Dispose 方法实现的。这种方法是同步的。整个 Rx 库都建立在这个接口之上。因此,即使您为 DisposeAsync 创建扩展方法 IDisposable,内置 Rx 运算符(例如 SelectSelectManyWhere、{{ 1}} 等)将不知道它的存在,并且在他们取消订阅源序列时不会调用它。运营商订阅链将一如既往地通过调用上一个链接的同步 Take 方法自动取消链接。

顺便说一句,有人尝试实现 Rx (AsyncRx) 的异步版本,该版本建立在如下所示的全新接口之上。这个图书馆has not been released yet

Dispose