簡體   English   中英

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

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

如果你換出AsyncFinallyFinally ,你會崩潰的過程。

它在 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 操作符(例如SelectSelectManyWhereTake等)也不會意識到它的存在,並且不會在取消訂閱源序列時調用它. 一個訂閱鏈會像往常一樣通過調用上一個鏈接的同步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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM