There is a real-time sequence of instrumental observations IObservable<S>
. There is also an async
consumer function f : S -> Task<U>
(with side effects) that may take time to process a value. The function must not be reentered. As soon as it has returned, however, the next available sample from the stream should be passed to it. Additionally, the function must see the very last sample at the end of the stream.
It feels like this must be a pretty common pattern, but I cannot find an idiomatic solution. My approach works, but rather reeks of inelegance of using a subject as an imperative signal variable in the feedback loop. Is there an idiomatic solution to my problem?
static void Main() {
var source = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(27);
var s = source.Publish(ps => {
var signal = new Subject<Unit>();
return ps
.SkipLast(1)
.Window(signal)
.SelectMany(w => w.Take(1))
.Merge(ps.PublishLast().RefCount())
.Select(x => Observable.FromAsync(() => LengthyWork(x)))
.Concat()
.Do(signal.OnNext);
});
var d = s.Subscribe();
Thread.Sleep(5000);
Console.WriteLine("Stopping");
d.Dispose();
}
static async Task<Unit> LengthyWork(long n) {
Console.WriteLine($"Processing {n}");
await Task.Delay(800);
return Unit.Default;
}
EDIT: The code actually does not solve the problem in question: .Merge()
composition with the published last element pushes that element out of sync. In this example terms, 26
is send before the function returns from processing 24
.
Output (note that item 26 is the last before end of stream).
Processing 0
Processing 8
Processing 16
Processing 24
Processing 26
Stopping