Как динамически задерживать наблюдаемое с течением времени - PullRequest
0 голосов
/ 26 октября 2018

В настоящее время у меня есть функция, позволяющая пользователям Timer() запускать наблюдаемое немедленно, а затем каждые x миллисекунд после.

HoldPayloads = Observable.Merge(
    EnumeratedSymbolKeys.Select(
        o => Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonDown += h,
            h => o.PreviewMouseLeftButtonDown -= h)
            .Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency))
            .TakeUntil(Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                h => o.PreviewMouseLeftButtonUp += h,
                h => o.PreviewMouseLeftButtonUp -= h)))
                .Switch()
                .Select(_ => o.Payload)));

То, что я хотел бы иметь, это наблюдаемый огонь сразу после нажатия кнопки, затем после начальной более длительной задержки начинают повторяться либо с более быстрым интервалом, либо с уменьшающимся интервалом до предела, что-то вроде этого:

--x------x--x--x--x--x--x-->

или

--x------x----x---x--x-x-x->

Я попытался использовать Delay() в сочетании с Scan(), чтобы сгенерировать экспоненциально более низкие значения для задержки, но не смог заставить его работать. Я был на правильном пути? Есть ли лучшие способы сделать что-то подобное?

Пересмотрен код с ответом от Шломо:

HoldPayloads = Observable.Merge(
EnumeratedSymbolKeys.Select(
    o => Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
        h => o.PreviewMouseLeftButtonDown += h,
        h => o.PreviewMouseLeftButtonDown -= h)
        .Select(_ => Observable.Generate(
            1,
            q => true,
            i => i+1,
            i => i,
            i => i==1
                ? TimeSpan.FromMilliseconds(0)
                : i > 10
                    ? TimeSpan.FromMilliseconds(50)
                    : TimeSpan.FromMilliseconds(500/i))
        .TakeUntil(Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonUp += h,
            h => o.PreviewMouseLeftButtonUp -= h)))
            .Switch()
            .Select(_ => o.Payload)));

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

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Я просто подумал, что просто добавлю ответ, чтобы сделать последний запрос более читабельным.Я никоим образом не изменил ответ, и я не хочу, чтобы этот ответ был принят или даже проголосовал.Я просто хотел помочь внести некоторую ясность в понимание запроса.

Я только что провел небольшой рефакторинг замещения и изменил запрос на:

HoldPayloads =
    EnumeratedSymbolKeys
        .Select(o =>
            MouseDowns(o)
                .Select(_ => Generate().TakeUntil(MouseUps(o)))
                .Switch()
                .Select(_ => o.Payload))
        .Merge();

Для меняэто намного легче прочитать, и цель запроса очень ясна.

Он просто оставляет определение MouseDowns, MouseUps и Generate.Это:

IObservable<Unit> MouseDowns(EnumeratedSymbolKey o) =>
    Observable
        .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonDown += h, h => o.PreviewMouseLeftButtonDown -= h)
        .Select(EncodingProvider => Unit.Default);

IObservable<Unit> MouseUps(EnumeratedSymbolKey o) =>
    Observable
        .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
            h => o.PreviewMouseLeftButtonUp += h, h => o.PreviewMouseLeftButtonUp -= h)
        .Select(EncodingProvider => Unit.Default);          

IObservable<int> Generate() =>
    Observable
        .Generate(1, i => true, i => i + 1, i => i,
            i => i == 1
                ? TimeSpan.FromMilliseconds(0) 
                : (i > 10 ? TimeSpan.FromMilliseconds(50) : TimeSpan.FromMilliseconds(500 / i)));

Теперь, когда они отделены, легче подтвердить, что каждый из них правильный, и, надеюсь, весь код верен.

0 голосов
/ 26 октября 2018

Observable.Generate ваш друг (хорошо обсуждено на этой странице).

Для более частых значений замените .Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency)) на что-то вроде этого:

Observable.Generate(
    1, 
    _ => true,
    i => i + 1,
    i => i,
    i => i > 10
        ? TimeSpan.FromMilliseconds(50)
        : TimeSpan.FromMilliseconds(500 / i)
)            

Каким бы ни был ваш шаблон, поместите его в этот последний параметр. Думайте о Generate как о реактивном цикле for со всеми доступными вам циферблатами.


EDIT

С кодом выше, первый элемент из Generate будет отложен. Если вам нужен первый элемент немедленно, а второй элемент отложен, вы можете сделать следующее:

Observable.Generate(
    1, 
    _ => true,
    i => i + 1,
    i => i,
    i => i > 10
        ? TimeSpan.FromMilliseconds(50)
        : TimeSpan.FromMilliseconds(500 / i)
)            
.StartWith(0)

Итак, оригинал .Select(_ => Observable.Timer(DateTimeOffset.Now, TimeSpan.FromMilliseconds(o.Frequency) теперь .Select(_ => Observable.Generate(...).StartWith(0))

...