[英]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.