Scheduling a IEnumerable periodically with .NET reactive extensions

*爱你&永不变心* 提交于 2019-12-31 03:51:32

问题


Say for example I have an enumerable

dim e = Enumerable.Range(0, 1024)

I'd like to be able to do

dim o = e.ToObservable(Timespan.FromSeconds(1))

So that the observable would generate values every second until the enumerable is exhausted. I can't figure a simple way to do this.


回答1:


I have also looked for the solution and after reading the intro to rx made my self one: There is an Observable.Generate() overload which I have used to make my own ToObservable() extension method, taking TimeSpan as period:

public static class MyEx {
    public static IObservable<T> ToObservable<T>(this IEnumerable<T> enumerable, TimeSpan period) 
    {
        return Observable.Generate(
            enumerable.GetEnumerator(), 
            x => x.MoveNext(),
            x => x, 
            x => x.Current, 
            x => period);
    }
    public static IObservable<T> ToObservable<T>(this IEnumerable<T> enumerable, Func<T,TimeSpan> getPeriod) 
    {
        return Observable.Generate(
            enumerable.GetEnumerator(), 
            x => x.MoveNext(),
            x => x, 
            x => x.Current, 
            x => getPeriod(x.Current));
    }
}

Already tested in LINQPad. Only concerning about what happens with the enumerator instance after the resulting observable is e.g. disposed. Any corrections appreciated.




回答2:


You can use Interval together with Zip to get the desired functionality:

var sequence = Observable.Interval(TimeSpan.FromSeconds(1))
                         .Zip(e.ToObservable(), (tick, index) => index)



回答3:


You'd need something to schedule notifying observers with each value taken from the Enumerable. You can use the recursive Schedule overload on an Rx scheduler.

Public Shared Function Schedule ( _
    scheduler As IScheduler, _
    dueTime As TimeSpan, _
    action As Action(Of Action(Of TimeSpan)) _
) As IDisposable

On each scheduled invocation, simply call enumerator.MoveNext(), and call OnNext(enumerator.Current), and finally OnCompleted when MoveNext() returns false. This is pretty much the bare-bones way of doing it.

An alternative was to express your requirement is to restate it as "for a sequence, have a minimum interval between each value".

See this answer. The test case resembles your original question.




回答4:


You could always do this very simple approach:

dim e = Enumerable.Range(0, 1024)
dim o = e.ToObservable().Do(Sub (x) Thread.Sleep(1000))

When you subscribe to o the values take a second to be produced.




回答5:


I can only assume that you are using Range to dumb down your question.

Do you want every value that the Enumerable pushes to be delayed by a second?

var e = Enumerable.Range(0, 10);
var o = Observable.Interval(TimeSpan.FromSeconds(1))
                  .Zip(e, (_,i)=>i);

Or do you want only the last value of the Enumerable at each second to be pushed. i.e. reading from Enumerable that is evaluating as you enumerate it (perhaps some IO). In which case CombineLatest is more useful than Zip.

Or perhaps you just want to get a value every second, in which case just use the Observable.Interval method

var o = Observable.Interval(TimeSpan.FromSeconds(1));

If you explain your problem space then the community will be able to better help you.

Lee

*Excuse the C# answer, but I dont know what the equivalent VB.NET code would be.



来源:https://stackoverflow.com/questions/12692244/scheduling-a-ienumerable-periodically-with-net-reactive-extensions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!