Using Rx to execute delayed action once with cancellation for each window

北战南征 提交于 2019-12-11 06:57:45

问题


specifically I am trying to simulate performing an action if the key is held down for a duration greater than Threshold (T).

I am trying to do this using Reactive Extensions .NET (The stable 1.0 version) without state variables.

Here's the marble diagram of my inputs and what I need:

let T = 3 (so 4 dddd without a key up event make up a "key held down")

keyDown: --dddd---dd--d-dddddddddd----

keyUp: -----------u-----u--u---------------u--

desired: --------a---------------a----------

Here's some example code I came up with that works but uses a state variable.

var keyDownStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown").Where(args => args.EventArgs.Key == Key.OemPeriod);
        var keyUpStream = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp").Where(args => args.EventArgs.Key == Key.OemPeriod);

        var repeatGuard = false;
        keyUpStream.Subscribe(x => repeatGuard = false);
        keyDownStream
            .DelayOrCancel(TimeSpan.FromSeconds(2.0), keyUpStream)
            .Where(_ => repeatGuard == false)
            .Do(_ =>
            {
                repeatGuard = true;
            })
            .Subscribe(
                result =>
                {
                    Console.WriteLine("KeyHold");
                }
            );

public static class JustinsRx
{
    public static IObservable<T> DelayOrCancel<T, TCancel>(this IObservable<T> source,
                                     TimeSpan delay,
                                     IObservable<TCancel> cancel)
    {
        //argument checking skipped
        return from s in source
               from i in Observable.Timer(delay).TakeUntil(cancel)
               select s;
    }
}

回答1:


This works, but I feel that it could be made shorter.

var firstKeyDowns = Observable
    .Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
    .DistinctUntilChanged()
    .Where(c => c == 'd');
var query = from s in firstKeyDowns 
            from i in Observable.Timer(delay).TakeUntil(keyUpStream)
            select s;

EDIT: Here's a different version that I think is a bit nicer:

var noRepeats = Observable
    .Merge(keyDownStream.Select(_ => 'd'), keyUpStream.Select(_ => 'u'))
    .DistinctUntilChanged();
var query = noRepeats
    .Throttle(delay)
    .Where(c => c == 'd');



回答2:


This worked for me:

var timer = Observable.Timer(TimeSpan.FromSeconds(1.0));

var query =
    keyDownStream
        .Select(_ =>
            keyUpStream
                .Select(_ => 'u')
                .Amb(timer.Select(_ => 't'))
                .Take(1)
                .Where(x => x == 'u')
                .Select(_ => Unit.Default))
        .Switch();


来源:https://stackoverflow.com/questions/10013409/using-rx-to-execute-delayed-action-once-with-cancellation-for-each-window

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!