[英]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 justasync 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.如果你换出
AsyncFinally
的Finally
,你会崩溃的过程。
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 anAction
as a parameter.Finally
扩展方法接受一个Action
作为参数。 ThisAction
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 操作符(例如Select
、 SelectMany
、 Where
、 Take
等)也不会意识到它的存在,并且不会在取消订阅源序列时调用它. 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.