У меня есть наблюдаемая последовательность, которая создает события в быстрых всплесках (т.е. пять событий один за другим, затем длинная задержка, затем еще один быстрый всплеск событий и т.д.). Я хочу сгладить эти всплески, вставив короткую задержку между событиями. Представьте себе следующую диаграмму:
Raw: --oooo--------------ooooo-----oo----------------ooo| Buffered: --o--o--o--o--------o--o--o--o--o--o--o---------o--o--o|
Мой текущий подход состоит в том, чтобы генерировать метроноподобный таймер через Observable.Interval()
, который сигнализирует, когда это нормально, чтобы вытащить другое событие из необработанного потока. Проблема в том, что я не могу понять, как скомбинировать этот таймер с моей необработанной необнаруживаемой наблюдаемой последовательностью.
IObservable.Zip()
близок к выполнению того, что я хочу, но он работает только до тех пор, пока поток raw генерирует события быстрее, чем таймер. Как только в необработанном потоке происходит значительное затишье, таймер создает ряд нежелательных событий, которые затем сразу соединяются со следующим пакетом событий из необработанного потока.
В идеале я хочу использовать метод расширения IObservable со следующей сигнатурой функции, которая дает bevaior, который я изложил выше. Теперь, прибегите к моему спасению StackOverflow:)
public static IObservable<T> Buffered(this IObservable<T> src, TimeSpan minDelay)
PS. Я новичок в Rx, поэтому извиняюсь, если это простой вопрос...
1. Простой, но ошибочный подход
Здесь мое первоначальное наивное и упрощенное решение, имеющее немало проблем:
public static IObservable<T> Buffered<T>(this IObservable<T> source, TimeSpan minDelay)
{
Queue<T> q = new Queue<T>();
source.Subscribe(x => q.Enqueue(x));
return Observable.Interval(minDelay).Where(_ => q.Count > 0).Select(_ => q.Dequeue());
}
Первая очевидная проблема заключается в том, что IDisposable, возвращаемый внутренней подпиской на исходный источник, теряется, и поэтому подписка не может быть прервана. Вызов Dispose на IDisposable, возвращаемый этим методом, убивает таймер, но не базовый необработанный фид событий, который теперь бесполезно заполняет очередь, и никто не оставил, чтобы вывести события из очереди.
Вторая проблема заключается в том, что для исключений исключений или уведомлений о прекращении потока нет возможности передавать через поток необработанных событий в буферный поток - они просто игнорируются при подписке на исходный источник.
И последнее, но не менее важное: теперь у меня есть код, который периодически просыпается, независимо от того, есть ли на самом деле какая-либо работа, которую я бы предпочел избежать в этом замечательном новом мире реакции.
2. Путь чрезмерно сложный appoach
Чтобы решить проблемы, возникшие в моем первоначальном упрощенном подходе, я написал более сложную функцию much, которая ведет себя так же, как IObservable.Delay()
(я использовал .NET Reflector для чтения этого кода и использовал его как основы моей функции). К сожалению, многие логические схемы, такие как AnonymousObservable
, не доступны для публики вне системного кода. Поэтому мне пришлось копировать и вставлять код lot. Это решение, похоже, работает, но, учитывая его сложность, я менее уверен, что его ошибка бесплатна.
Я просто не могу поверить, что нет возможности выполнить это, используя некоторую комбинацию стандартных Reactive extensions. Ненавижу ощущение, что я совершенно не изобретаю колесо, и образец, который я пытаюсь построить, кажется довольно стандартным.