У меня есть проект, в котором мне нужно отправлять статусное сообщение каждые 10 секунд, если за это время не было обновлений. Это означает, что каждый раз, когда будет обновление, таймер сбрасывается.

var res = Observable
  .Interval(TimeSpan.FromSeconds(10))
  .Where(_ => condition);

res.Subscribe(_ => Console.WriteLine("Status sent."));

Теперь я знаю, что «Где» будет применяться только по окончании таймера, поэтому это не помогает. Но мне интересно, есть ли способ сбросить Интервал; или использовать Timer () с повторением.

4
joniboy 21 Окт 2015 в 04:05

3 ответа

Лучший ответ

Это довольно легко реализовать с помощью стандартных операторов Rx.

Из вашего вопроса неясно, что именно такое «обновление». Я собираюсь предположить, что у вас есть какой-то наблюдаемый объект, который запускается при каждом обновлении, или что вы можете создать тему, которую вы назовете .OnNext(...), когда будет обновление. Без наблюдаемых обновлений трудно понять, когда сбрасывать таймер.

Итак, вот код:

var update = new Subject<bool>();

var res =
    update
        .Select(x => Observable.Interval(TimeSpan.FromSeconds(10.0)))
        .Switch();

res
    .Subscribe(_ => Console.WriteLine("Status sent."));

update.OnNext(true);

Запрос res теперь ожидает, пока не получит значение из update, а затем выбирает новый Observable.Interval. Это означает, что после Select типом является IObservable<IObservable<long>>, поэтому .Switch() требуется, чтобы превратить его в IObservable<long>. .Switch() делает это, передавая только значения из последних наблюдаемых наблюдаемых и удаляя любые предыдущие наблюдаемые. Другими словами, для каждого update запускается новый таймер, а предыдущий таймер отменяется. Это означает, что если обновления происходят чаще, чем 10 секунд, таймер никогда не сработает.

Теперь, если наблюдаемая res сама по себе является обновлением, вы можете сделать это:

res
    .Subscribe(_ =>
    {
        update.OnNext(true);
        Console.WriteLine("Status sent.");
    });

Это нормально - это все еще работает, но для каждого срабатывания таймера res будет создавать новый таймер. Это будет означать, что все, что полагается на ваш update наблюдаемый / объект, по-прежнему будет работать правильно.

6
Enigmativity 21 Окт 2015 в 07:50

Думаю, здесь можно обойтись и Throttle. Цель Throttle - не допустить, чтобы элементы были получены в течение заданного промежутка времени. Поэтому в вашем случае, если сообщение об обновлении получено в течение 10 секунд, не отправляйте статус. См. Ниже модульный тест, в котором в качестве периода дросселирования используется 200 тиков.

[TestMethod]
    public void Publish_Status_If_Nothing_Receieved()
    {
        //Arrange
        var scheduler = new TestScheduler();
        var statusObserver = scheduler.CreateObserver<Unit>();
        var updateStream = scheduler.CreateColdObservable(OnNext(100, 1), OnNext(200, 2), OnNext(600, 3),
            OnNext(700, 4));

        var waitTime = TimeSpan.FromTicks(200);

        //Act
        updateStream.Throttle(waitTime, scheduler)
            .Select(_ => Unit.Default)
            .Subscribe(statusObserver);

        //Verify no status received
        scheduler.AdvanceTo(100);
        Assert.AreEqual(0, statusObserver.Messages.Count);

        //Verify no status received
        scheduler.AdvanceTo(200);
        Assert.AreEqual(0, statusObserver.Messages.Count);

        //Assert status received
        scheduler.AdvanceTo(400);
        statusObserver.Messages.AssertEqual(OnNext(400, Unit.Default));

        //Verify no more status received
        scheduler.AdvanceTo(700);
        statusObserver.Messages.AssertEqual(OnNext(400, Unit.Default));
    }
1
user630190 21 Окт 2015 в 08:09

Я держу с собой этот небольшой вспомогательный метод:

public static IObservable<long> CreateAutoResetInterval<TSource>(IObservable<TSource> resetter, TimeSpan timeSpan, bool immediate = false)
{
    return resetter.Select(_ => immediate ? Observable.Interval(timeSpan).StartWith(0) : Observable.Interval(timeSpan)).Switch();
}

Это в основном тот же механизм, что и ответ Enigmativity.

2
Felix Keil 27 Мар 2018 в 17:47