[英]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");
}
如果你换出AsyncFinally
的Finally
,你会崩溃的过程。
它在 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 操作符(例如Select
、 SelectMany
、 Where
、 Take
等)也不会意识到它的存在,并且不会在取消订阅源序列时调用它. 一个订阅链会像往常一样通过调用上一个链接的同步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.