繁体   English   中英

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

[英]Alternative to using async () => in Rx Finally

我的理解是async void应该被避免,并且async () =>Action使用时只是伪装的async void

因此,应避免将 Rx.NET finally 运算符async () =>异步使用, 因为 finally接受Action作为参数:

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

但是,如果这是不好的做法,那么在例如我需要异步关闭 OnCompleted 上的网络连接或者我的可观察对象以 OnError 结束的情况下,最佳做法是什么?

我的理解是应该避免 async void,而async () =>只是伪装的async void

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

所以我们只需要编写一个AsyncFinally操作符,它接受一个Func<Task>而不是像Observable.Finally这样的Action

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");
}

如果你换出AsyncFinallyFinally ,你会崩溃的过程。

它在 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() )以防止多个订阅。

Finally的方法签名是

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

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

作为补充,如果您想异步运行某些东西,而不是 async void, Task.Factory方法内使用Task.Factory方法,以便明确意图。

引自Intro to Rx

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

(强调)

此行为不能被复制Finally操作者接受一个Func<Task>因为如何参数, IObservable<T>接口被定义。 取消订阅可观察序列是通过调用IDisposable订阅的Dispose方法实现的。 这种方法是同步的。 整个 Rx 库都建立在这个接口之上。 因此,即使您为IDisposable创建了扩展方法DisposeAsync ,内置的 Rx 操作符(例如SelectSelectManyWhereTake等)也不会意识到它的存在,并且不会在取消订阅源序列时调用它. 一个订阅链会像往常一样通过调用上一个链接的同步Dispose方法自动取消链接。

顺便说一句,有人尝试实现 Rx ( AsyncRx ) 的异步版本,该版本建立在如下所示的全新接口之上。 这个库还没有发布

public interface IAsyncObserver<in T>
{
    ValueTask OnNextAsync(T value);
    ValueTask OnErrorAsync(Exception error);
    ValueTask OnCompletedAsync();
}

public interface IAsyncObservable<out T>
{
    ValueTask<IAsyncDisposable> SubscribeAsync(IAsyncObserver<T> observer);
}

public interface IAsyncDisposable
{
    public ValueTask DisposeAsync();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM