Scheduling a IEnumerable periodically with .NET reactive extensions

后端 未结 5 486
闹比i
闹比i 2021-01-24 05:21

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(Timesp         


        
相关标签:
5条回答
  • 2021-01-24 05:34

    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.

    0 讨论(0)
  • 2021-01-24 05:35

    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.

    0 讨论(0)
  • 2021-01-24 05:39

    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.

    0 讨论(0)
  • 2021-01-24 05:43

    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.

    0 讨论(0)
  • 2021-01-24 05:59

    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)
    
    0 讨论(0)
提交回复
热议问题