简体   繁体   English

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

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

My understanding is that async void , should be avoided and that async () => is just async void in disguise when used with Action .我的理解是async void应该被避免,并且async () =>Action使用时只是伪装的async void

Hence, using the Rx.NET Finally operator asynchronously with async () => should be avoided since Finally accepts Action as parameter:因此,应避免将 Rx.NET finally 运算符async () =>异步使用, 因为 finally接受Action作为参数:

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

However, if this is bad practise, what is then best practice to use in the case where I for instance need to asynchronously close a network connection on OnCompleted or if my observable end with OnError?但是,如果这是不好的做法,那么在例如我需要异步关闭 OnCompleted 上的网络连接或者我的可观察对象以 OnError 结束的情况下,最佳做法是什么?

My understanding is that async void, should be avoided and that async () => is just async void in disguise.我的理解是应该避免 async void,而async () =>只是伪装的async void

This is partially wrong.这是部分错误的。 async () => can either match Func<Task> (good) or Action (bad). async () =>可以匹配Func<Task> (good) 或Action (bad)。 The main reason for good/bad is that an exception that occurs in a async void call crashes the process, whereas a async Task exception is catchable.好/坏的主要原因是async void调用中发生的异常使进程崩溃,而async Task异常是可捕获的。

So we just need to write an AsyncFinally operator that takes in a Func<Task> instead of an Action like Observable.Finally :所以我们只需要编写一个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()
        ;
    }
}

And here's a demonstration of usage:这是用法演示:

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

If you swap out AsyncFinally for Finally , you'll crash the process.如果你换出AsyncFinallyFinally ,你会崩溃的过程。

It is in Rx as it is elsewhere;它在 Rx 中就像在其他地方一样; avoid async void like the plague.避免像瘟疫一样的async void In addition to the problems listed in the article, using asynchronous code in the synchronous operators "breaks" Rx.除了文章中列出的问题,在同步操作符中使用异步代码“破坏”了 Rx。

I'd consider using OnErrorResumeNext() for cleaning up resources asynchronously.我会考虑使用OnErrorResumeNext()异步清理资源。 OnErrorResumeNext() let's you specify an observable which will run after the first, regardless the reason it ended: OnErrorResumeNext()让我们指定一个将在第一个之后运行的 observable,无论它结束的原因是什么:

var myObservable = ...

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

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

myObservable would preferably be a ConnectableObservable (eg Publish() ) to prevent multiple subscriptions. myObservable最好是一个ConnectableObservable (例如Publish() )以防止多个订阅。

The method signature for Finally is Finally的方法签名是

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

which expects an action, not a Task.它期望一个动作,而不是一个任务。

As an addendum, if you want to run something asynchronously, instead of async void, use Task.Factory methods inside the method so the intention is explicit.作为补充,如果您想异步运行某些东西,而不是 async void, Task.Factory方法内使用Task.Factory方法,以便明确意图。

Quoting from the Intro to Rx :引自Intro to Rx

The Finally extension method accepts an Action as a parameter. Finally扩展方法接受一个Action作为参数。 This Action will be invoked if the sequence terminates normally or erroneously, or if the subscription is disposed of .如果序列正常终止或错误终止,或者订阅被销毁,则将调用此Action

(emphasis added) (强调)

This behavior cannot be replicated by a Finally operator that accepts a Func<Task> parameter, because of how the IObservable<T> interface is defined.此行为不能被复制Finally操作者接受一个Func<Task>因为如何参数, IObservable<T>接口被定义。 Unsubscribing from an observable sequence is achieved by calling the Dispose method of the IDisposable subscription.取消订阅可观察序列是通过调用IDisposable订阅的Dispose方法实现的。 This method is synchronous.这种方法是同步的。 And the whole Rx library is built on top of this interface.整个 Rx 库都建立在这个接口之上。 So even if you create an extension method DisposeAsync for IDisposable s, the built-in Rx operators (for example Select , SelectMany , Where , Take etc) will be unaware of its existence, and will not invoke it when they unsubscribe from their source sequence.因此,即使您为IDisposable创建了扩展方法DisposeAsync ,内置的 Rx 操作符(例如SelectSelectManyWhereTake等)也不会意识到它的存在,并且不会在取消订阅源序列时调用它. A subscription chain of operators will be automatically unlinked by calling the synchronous Dispose method of the previous link as always.一个订阅链会像往常一样通过调用上一个链接的同步Dispose方法自动取消链接。

Btw there has been an attempt to implement an asynchronous version of Rx ( AsyncRx ), that is built on top of the completely new interfaces that are shown below.顺便说一句,有人尝试实现 Rx ( AsyncRx ) 的异步版本,该版本建立在如下所示的全新接口之上。 This library has not been released yet .这个库还没有发布

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