我的理解是,与async void
一起使用时,async () =>
,should be avoided和async void
只是伪装成Action
。
因此,应避免与async () =>
异步使用the Rx.NET Finally operator,因为最终接受Action
作为参数:
IObservable<T>.Finally(async () =>
{
await SomeCleanUpCodeAsync();
};
但是,如果这是不好的做法,那么在我需要异步关闭网络coOnCompleted问题的情况下,如果我的observable是OnError或OnCompleted,那么最佳做法是什么?
答案 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 运算符(例如 Select
、SelectMany
、Where
、{{ 1}} 等)将不知道它的存在,并且在他们取消订阅源序列时不会调用它。运营商订阅链将一如既往地通过调用上一个链接的同步 Take
方法自动取消链接。
顺便说一句,有人尝试实现 Rx (AsyncRx) 的异步版本,该版本建立在如下所示的全新接口之上。这个图书馆has not been released yet。
Dispose