问题
I have the below types ...
public class NewsFeed
{
public event EventHandler<NewsItemEventArgs> NewItem;
.....
}
public class NewsItemEventArgs : EventArgs
{
public NewsItem Item;
public NewsItemEventArgs(NewsItem newsItem)
{
Item = newsItem;
}
}
public class NewsItem
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
}
The NewsFeed's NewItem event fires events with eventArgs of type NewsItemEventArgs. In my system the events are published in bursts, say 10 NewsItems in a small 1 second window and then no further news stories for 60 seconds. I would like to smooth these bursts out with RX so my in my UI news stories 'appear' to arrive one at a time in more regular intervals of say 5 seconds.
I know I need to create an observable with something like the below
_source = Observable.FromEventPattern<NewsItemEventArgs>(
h => _newsFeed.NewItem += h,
h => _newsFeed.NewItem -= h);
but I dont know how to transform and subscribe to the observable such that I get drip fed the events rather than them coming in the bursts described above.
Any ideas?
回答1:
You could zip your sequence with another which produces values at regular intervals:
Observable<NewsItem> nis = _source
.Zip(Observable.Timer(Timespan.FromSeconds(5), TimeSpan.FromSeconds(5)), (e, _) => e)
.Select(eventArgs => eventArgs.Item);
回答2:
Zip might not be the best choice for this operation because there's a chance for the producer to be slow at times, resulting a jittery output.
It seems accurate scheduling with DateTimeOffset still isn't possible with Rx 2.0. TimeSpan
works for now, though. You can try it by replacing the TimeSpan
offset with a DateTimeOffset
.
In summary, if we can specify a minimum interval between two consecutive values, we can solve the burst problem.
static IObservable<T> DelayBetweenValues<T>(this IObservable<T> observable, TimeSpan interval, IScheduler scheduler)
{
return Observable.Create<T>(observer =>
{
var offset = TimeSpan.Zero;
return observable
.TimeInterval(scheduler)
.Subscribe
(
ts =>
{
if (ts.Interval < interval)
{
offset = offset.Add(interval);
scheduler.Schedule(offset, () => observer.OnNext(ts.Value));
}
else
{
offset = TimeSpan.Zero;
observer.OnNext(ts.Value);
}
}
);
});
}
Test:
Observable.Interval(TimeSpan.FromSeconds(2.5))
.Do(_ => Console.WriteLine("Burst"))
.SelectMany(i => Enumerable.Range((int)i, 10))
.DelayBetweenValues(TimeSpan.FromSeconds(0.2), TaskPoolScheduler.Default)
.Subscribe(Console.WriteLine);
回答3:
I couldn't get the answer from Asti to work. So I tried the undocumented Delay overload with the delayDurationSelector. I got it working for my problem, though somehow it's not behaving correctly when I use the scheduler in the timer, but it works without it:
public static IObservable<T> DelayBetweenValues<T>(this IObservable<T> observable, TimeSpan interval,
IScheduler scheduler)
{
var offset = TimeSpan.Zero;
return observable
.TimeInterval(scheduler)
.Delay(ti =>
{
offset = (ti.Interval < interval) ? offset.Add(interval) : TimeSpan.Zero;
return Observable.Timer(offset);
})
.Select(ti => ti.Value);
}
来源:https://stackoverflow.com/questions/12651806/get-next-event-in-sequence-every-second-with-reactive-extensions