簡體   English   中英

測試取消一個簡單的observable不會調用onError

[英]testing the cancellation of a simple observable doesn't call onError

鑒於以下內容:

為什么SubscribeOnError處理程序永遠不會被調用?

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.

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