[英]How to handle the exception thrown by the async method with observable?
我有一個 observable,我想用異步方法訂閱這個 observable,但是每次異步方法拋出異常時,即使我將 catch 代碼放在 observable 定義中,訂閱也會立即處理。 下面的偽代碼演示了這種情況:
[Fact]
public async Task Test()
{
var observable = Observable.Create<int>(observer =>
{
try
{
Enumerable.Range(1, 10).ToList().ForEach(x =>
{
observer.OnNext(x);
});
}
catch (Exception ex)
{
// get called after the exception is thrown
_testOutputHelper.WriteLine($"The exception is catch:{ex.ToString()}");
}
return Disposable.Create(() =>
{
// also get called after exception is thrown
_testOutputHelper.WriteLine("Observable Dispose");
});
});
Func<int, Task> handler = async (i) =>
{
// simulate the handler logic
await Task.Delay(TimeSpan.FromSeconds(1));
// throw the exception to test
throw new Exception($"{i}");
};
observable.Subscribe(x=>handler(x).Wait());
await Task.Delay(TimeSpan.FromSeconds(10));
}
從上面的代碼中,我不明白為什么即使捕獲異常也會調用 dispose 委托(出於某種原因,我必須在可觀察定義中處理異常),有沒有辦法防止訂閱在異步方法拋出異常?
您的代碼完全按照您的要求執行。
捕獲異常的目的是讓您的程序可以繼續運行而不會突然停止。 這正是您的代碼正在做的事情:異常被捕獲,然后在catch
塊之后繼續執行。
如果你想讓它做其他事情,你有兩個選擇。
catch (Exception ex)
{
// get called after the exception is thrown
_testOutputHelper.WriteLine($"The exception is catch:{ex.ToString()}");
throw;
}
然后,任何稱為Test()
的代碼都將負責捕獲該異常(或不捕獲)。
try
塊內,並在捕獲到異常時返回其他內容:try
{
Enumerable.Range(1, 10).ToList().ForEach(x =>
{
observer.OnNext(x);
});
return Disposable.Create(() =>
{
// also get called after exception is thrown
_testOutputHelper.WriteLine("Observable Dispose");
});
}
catch (Exception ex)
{
// get called after the exception is thrown
_testOutputHelper.WriteLine($"The exception is catch:{ex.ToString()}");
return //something else
}
您可能會受益於閱讀 Microsoft 的異常處理文檔。
您的代碼中發生的事情是您使用Observable.Create
並使用以下代碼填充 observable 的直接結果:
Enumerable.Range(1, 10).ToList().ForEach(x =>
{
observer.OnNext(x);
});
Observable.Create
使用當前線程創建 observable,因此Enumerable.Range(1, 10).ToList().ForEach
立即在當前線程上執行,對OnNext
的調用立即執行handler(x).Wait()
.
但是,您會注意到,異常發生在傳遞給Subscribe
的委托中。 內部有這樣的代碼:
catch (Exception exception)
{
if (!autoDetachObserver.Fail(exception))
{
throw;
}
return autoDetachObserver;
}
這會在訂閱中捕獲異常,取消訂閱 - 因此是"Observable Dispose"
消息 - 然后重新拋出異常,這就是您的代碼捕獲它的地方。
現在,如果您想在 Rx 中正確執行此操作,則應避免使用Observable.Create
。 這是一種創建 observables 的誘人方式,但它會帶來麻煩。
而是這樣做:
public async Task Test()
{
Func<int, Task> handler = async (i) =>
{
// simulate the handler logic
await Task.Delay(TimeSpan.FromSeconds(1));
// throw the exception to test
throw new Exception($"{i}");
};
await
Observable
.Range(1, 10)
.SelectMany(i => Observable.FromAsync(() => handler(i)))
.LastOrDefaultAsync();
}
但是,當然,我們要處理異常。 簡單的方法是這樣的:
public async Task Test()
{
Func<int, Task> handler = async (i) =>
{
// simulate the handler logic
await Task.Delay(TimeSpan.FromSeconds(1));
// throw the exception to test
throw new Exception($"{i}");
};
await
Observable
.Range(1, 10)
.SelectMany(i =>
Observable
.FromAsync(() => handler(i))
.Catch<Unit, Exception>(ex =>
{
Console.WriteLine($"The exception is catch:{ex.ToString()}");
return Observable.Empty<Unit>();
}))
.LastOrDefaultAsync();
}
現在輸出 10 個異常錯誤並正常完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.