所有订阅者完成后重复RX重复

时间:2017-12-11 11:55:39

标签: c# system.reactive

在RX中是否有办法使用repeat观察形式异步,并且只有在所有订阅完成后才调用下一条消息? 例如:

var messages = new Stack<int>(new int[] { 1, 2, 3, 4, 5, 6 });

var observable = Observable
    .FromAsync(() => Task.Run(() => messages.Peek()))
    .Repeat(6);

observable.Subscribe(async message =>
{
    await Task.Delay(2);
    Console.WriteLine(message);
    messages.Pop();
});

当前输出:666555

所需的输出:654321

完成所有处理后删除邮件的原因是为了确保处理所有邮件。

我知道通过使订阅同步可以实现所需的输出,异步方法可以实现相同的效果吗?

1 个答案:

答案 0 :(得分:2)

所以我会这样做(如果我坚持使用Task.Run):

var messages = new Stack<int>(new int[] { 1, 2, 3, 4, 5, 6 });

var observable =
    Observable.While<int>(
        () => messages.Count > 0,
        Observable.Defer(() =>
            Observable.FromAsync(() =>
                Task.Run(() => messages.Pop()))));

observable
    .Subscribe(async message =>
    {
        await Task.Delay(2);
        Console.WriteLine(message);
    }); 

这会产生如下运行:

3
2
6
5
1
4

每当您看到Observable.FromAsync(() => Task.Run(() => /* something */))时,您几乎总是将其替换为Observable.Start(() => /* something */)。如果你不需要,可以在一个observable中调用任务。

代码是:

var observable =
    Observable.While<int>(
        () => messages.Count > 0,
        Observable.Defer(() =>
            Observable.Start(() => messages.Pop())));

如果你想要在值之间有一个延迟而你想要它们,那么试试这个:

var observable =
    Observable.While<int>(
        () => messages.Count > 0,
        Observable.Defer(() =>
            Observable
                .Start(() => messages.Pop())
                .Delay(TimeSpan.FromSeconds(2.0))));

observable
    .Subscribe(message => Console.WriteLine(message));

或者,有一个选项可能对您更好。试试这个:

var observable =
    Observable
        .Generate(
            0,
            n => messages.Count > 0,
            n => n + 1,
            n => messages.Pop(),
            n => TimeSpan.FromSeconds(2.0));

observable
    .Subscribe(message => Console.WriteLine(message));

.Generate运算符非常强大。

如果您希望立即生成第一个值,可以像这样更改:

var observable =
    Observable
        .Generate(
            0,
            n => messages.Count > 0,
            n => n + 1,
            n => messages.Pop(),
            n => TimeSpan.FromSeconds(n == 0 ? 0.0 : 2.0));