惯用的Rx与否?

时间:2014-05-20 21:00:25

标签: .net system.reactive

小问题 - 这个代码是惯用的Rx还是没有 - 如果没有,应该改变什么呢?注意 - 代码在LINQPad中用于测试目的,以后可能用作"样板":

IObservable<byte[]> GenerateRandomDataChunks(IScheduler scheduler, int chunkSize = 10)
{
    return
        Observable.Create<byte[]>(
            observer => {
                var cancel = new CancellationDisposable();
                scheduler.Schedule(() => {
                    try // outer capture of exceptions => OnError
                    {
                        var rnd = new Random();
                        while (!cancel.Token.IsCancellationRequested) // => cancellation
                        {
                            scheduler.Yield();

                            var ms = rnd.Next(100, 500);
                            Thread.Sleep(ms); // introduce artificial lag for testing purposes
                            //scheduler.Sleep(TimeSpan.FromMilliseconds(ms)); // test simulation doesn't work - why not?

                            var data = new byte[chunkSize];
                            rnd.NextBytes(data);

                            //var r = rnd.Next();
                            //if (r > 450 && r < 460)
                            //  throw new Exception("foobar");

                            observer.OnNext(data); // give back next computed value => OnNext
                        }
                        observer.OnCompleted(); // terminated naturally => OnCompleted
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex); // handle exception => OnError
                    }
                    finally
                    {
                        // TODO dispose any resources we might have
                    }
                });
                return cancel;
            }
        );
}

取消似乎很有效。出于测试目的,我尝试插入延迟时间,但它没有使用IScheduler.Sleep(示例ThreadPoolScheduler)。

1 个答案:

答案 0 :(得分:2)

您可以像这样使用Observable.Generate。请注意,在此变体和下一个变量中,如果没有提供调度程序,则提供调度程序,并且按顺序(以及更加惯用)的方式更改调度程序的最后顺序:

IObservable<byte[]> GenerateRandomDataChunks(
    int chunkSize = 10, IScheduler scheduler = null
{
    var rnd = new Random();
    return Observable.Generate<object,byte[]>(
        null, _ => true, _ => _, _ => {
            var data = new byte[chunkSize];
            rnd.NextBytes(data);
            return data;
        },
        // delete next line entirely to remove lag
        _ => TimeSpan.FromMilliseconds(rnd.Next(100, 500)), 
        scheduler ?? Scheduler.Default);
}

您遵循的方法也可以起作用,但需要整理一下。您不需要OnComplete或OnError。处理订阅仅表示OnNext呼叫应该停止。 async / await语法也使事情变得更清晰,并为您提供与订阅生命周期相关的取消令牌。这是由Observable.Create为您处理的。你绝对不想打电话给Thread.Sleep - 虽然我很感激这是为了测试 - 我在下面显示了Scheduler.Sleep的正确用法。

IObservable<byte[]> GenerateRandomDataChunks(
    int chunkSize = 10, IScheduler scheduler = null)
{
    var rnd = new Random();
    scheduler = scheduler ?? Scheduler.Default;

    return Observable.Create<byte[]>(async (o, ct) => {
        while(!ct.IsCancellationRequested)
        {
            var ms = rnd.Next(100, 500);
            await scheduler.Sleep(TimeSpan.FromMilliseconds(ms), ct);

            // replace the above with this to yield instead of lag
            // await scheduler.Yield(ct);

            ct.ThrowIfCancellationRequested();

            var data = new byte[chunkSize];
            rnd.NextBytes(data);
            o.OnNext(data);
        }
    });
}