[英]testing the cancellation of a simple observable doesn't call onError
鑒於以下內容:
為什么Subscribe
的OnError
處理程序永遠不會被調用?
var observable = Observable.Create<string>(
async (o, c) =>
{
try
{
var strings = new[] { "A", "B", "C" };
foreach (var s in strings)
{
await Task.Delay(100);
if (c.IsCancellationRequested)
{
// exception thrown here.
Console.WriteLine("cancelled");
throw new OperationCancelledException();
}
o.OnNext(s);
}
o.OnCompleted();
}
catch (Exception ex)
{
// caught here...
o.OnError(ex);
}
});
var tcs = new TaskCompletionSource<bool>();
var token = new CancellationTokenSource();
observable.Subscribe(
str =>
{
Console.WriteLine(str);
token.Cancel(); // cancel after the first iteration.
},
(e) =>
{
// why is this never called.
Console.WriteLine($"on error :: {e.Message}");
tcs.SetResult(true);
},
() =>
{
Console.WriteLine("on complete");
tcs.SetResult(true);
},
token.Token);
// code hangs here because the subscription never completes?
await tcs.Task;
Console.WriteLine("done");
當您在令牌上調用Cancel
時,您(通過令牌並因此“擁有”取消的訂戶)基本上說“我不再對事件感興趣,包括OnError()”。
在封面下,Rx在observable和觀察者之間插入一個AutoDetachObserver
,它明確地吞下所有其他事件。
此行為是設計使然。
在訂閱時, OnError()
會告訴您有關失敗的信息。 取消令牌(取消訂閱觀察者)后,您不再訂閱並且不再接收任何事件。 換句話說,取消訂閱不是錯誤。 並且取消observable中的令牌不是錯誤或通信的有效方式 - 調用OnError()
是。
在觀察者OnError
(以及其他函數)的基礎實現中包含:
if (Interlocked.Exchange(ref this.isStopped, 1) == 0)
{
this.OnErrorCore(error);
}
當令牌取消時,值isStopped
被設置為“已停止”。 觀察者負責取消過程,並且不需要手動控制它。
如果將OnNext
代碼更改為,則可以輕松檢查
if(str == "B")
token.Cancel(); // cancel after the second iteration.
結果將是:
即使你刪除了if
語句。 取消令牌后,不會調用任何繼承的函數
var observable = Observable.Create<string>(
async (o, c) =>
{
var strings = new[] { "A", "B", "C" };
foreach (var s in strings)
{
await Task.Delay(100);
o.OnNext(s);
}
o.OnCompleted();
});
因此請記住,當令牌被取消時(邏輯上它不是錯誤)不要將邏輯移動到任何事件實現,而是在調用者代碼中執行所需的取消邏輯:
if (c.IsCancellationRequested)
{
// exception thrown here.
Console.WriteLine("cancelled");
tcs.SetResult(true); // instead of throwing exceptions
// some other clean up code or/and return statement
}
建立詹姆斯寫的內容:Rx訂閱有兩個部分,即可觀察者(發布者)和觀察者(訂閱者)。 當您使用取消令牌訂閱 observable時,您實際上是在告訴訂閱者取消訂閱。
在您的方案中,這意味着一旦取消,訂閱者就會停止收聽。 因此不會監聽onError
通知,因此Task
永遠不會完成。
如果刪除傳遞給訂閱調用的token
,則onError
將按預期流向訂閱。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.