簡體   English   中英

非重播熱觀察

[英]Non replaying hot observable

原始問題

我有一個場景,我有多個IObservable序列,我想與Merge結合,然后聽。 但是,如果其中一個產生錯誤,我不希望它崩潰其他流的所有內容,以及重新訂閱序列(這是一個'永久'序列)。

我通過在合並之前向流添加Retry()來執行此操作,即:

IEnumerable<IObservable<int>> observables = GetObservables();

observables
    .Select(o => o.Retry())
    .Merge()
    .Subscribe(/* Do subscription stuff */);

但是,當我想測試時會出現問題。 我想測試的是,如果observables一個IObservable產生OnError ,其他的仍應該能夠發送它們的值並且它們應該被處理

我以為我只是使用兩個Subject<int>代表兩個observables IObservable ; 一個發送OnError(new Exception()) ,另一個發送OnNext(1) 但是,似乎Subject<int>將重放新訂閱的所有先前值(實際上是Retry() ),將測試轉換為無限循環。

我嘗試通過創建一個手動IObservable來解決它,該手動IObservable在第一個訂閱時產生錯誤,后來又是一個空序列,但它感覺很hacky:

var i = 0;
var nErrors = 2;
var testErrorObservableWithOneErrorAndThenCompletion = Observable.Create<int>(o => {
    i++;
    if (i < nErrors) {
        return Observable.Throw<int>(new Exception()).Subscribe(o);
    } else {
        return Observable.Empty<int>().Subscribe(o);
    }
});

我使用Subject還是以錯誤的方式思考Retry() 還有其他想法嗎? 你會如何解決這種情況?

更新

好的,這是我想要的大理石圖,並認為 Retry()作用。

o = message, X = error.
------o---o---X
               \
     Retry() -> \---o---o---X
                             \
                   Retry() -> \...

我的問題可能更多,因為我沒有一個好的庫存類來使用前測試,因為Subject希望重播我之前的所有錯誤。

更新2

這是一個測試用例,顯示了我對Subject重放其值的意思。 如果我說它以冷的方式做到這一點,我是否正確使用該術語? 我知道Subject是一種創造熱的可觀察性的方式,但這種行為對我來說仍然感覺“冷”。

var onNext = false;
var subject = new Subject<int>();

subject.Retry().Subscribe(x => onNext = true);
subject.OnError(new Exception());
subject.OnNext(1);

Assert.That(onNext, Is.True);

根據您更新的要求(您想要重試失敗的可觀察對象,而不是僅僅想忽略它們),我們可以提出一個有效的解決方案。

首先,理解冷可觀察(在每個訂閱上重新創建)和熱可觀察(無論訂閱是否存在)之間的區別是很重要的。 你不能Retry()一個熱的observable,因為它不知道如何重新創建底層事件。 也就是說,如果一個熱的可觀察錯誤,它就永遠消失了。

Subject創建一個熱的可觀察對象,在某種意義上你可以在沒有訂閱者的情況下調用OnNext ,它將按預期運行。 要將熱的observable轉換為冷可觀察對象,可以使用Observable.Defer ,它將包含該可觀察對象的“創建訂閱”邏輯。

總而言之,這是修改后的原始代碼:

var success = new Subject<int>();
var error = new Subject<int>();

var observables = new List<IObservable<int>> { Observable.Defer(() => {success = new Subject<int>(); return success.AsObservable();}), 
                                               Observable.Defer(() => {error = new Subject<int>(); return error.AsObservable();}) };                                            

observables
.Select(o => o.Retry())
.Merge()
.Subscribe(Console.WriteLine, Console.WriteLine, () => Console.WriteLine("done"));

和測試(類似於之前):

success.OnNext(1);
error.OnError(new Exception("test"));
success.OnNext(2);
error.OnNext(-1);
success.OnCompleted();
error.OnCompleted();

並按預期輸出:

1
2
-1
done

當然,您需要根據潛在的可觀察性來顯着修改此概念。 使用主題進行測試與將它們用於實際不同。

我還要注意這個評論:

但是,似乎Subject將重放新訂閱的所有先前值(實際上是Retry()),將測試變為無限循環。

不是真的 - Subject不會這樣做。 您的代碼還有一些其他方面導致無限循環基於Retry重新創建訂閱的事實,並且訂閱會在某個時刻創建錯誤。


原始答案 (完成)

問題是Retry()不能做你想做的事。 從這里:

http://msdn.microsoft.com/en-us/library/ff708141(v=vs.92).aspx

重復源可觀察序列retryCount次或直到它成功終止。

這意味着Retry將繼續嘗試重新連接到底層的observable,直到它成功並且不會拋出錯誤。

我的理解是你實際上希望observable中的異常被忽略 ,而不是重試。 這將做你想要的事情:

observables
.Select(o => o.Catch((Func<Exception,IObservable<int>>)(e => Observable.Empty<int>())))
.Merge()
.Subscribe(/* subscription code */);

這使用Catch來捕獲具有異常的observable,並在該點將其替換為空的observable。

以下是使用主題的完整測試:

var success = new Subject<int>();
var error = new Subject<int>();

var observables = new List<IObservable<int>> { success.AsObservable(), error.AsObservable() };

observables
.Select(o => o.Catch((Func<Exception,IObservable<int>>)(e => Observable.Empty<int>())))
.Merge()
.Subscribe(Observer.Create<int>(Console.WriteLine, Console.WriteLine, () => Console.WriteLine("done")));

success.OnNext(1);
error.OnError(new Exception("test"));
success.OnNext(2);
success.OnCompleted();

這正如預期的那樣產生:

1
2
done

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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