如何在RxJS中超时事件?

时间:2014-06-01 23:06:21

标签: rxjs

我正在尝试检测mousedown事件是否在mouseup之前保持一段时间。

我在使用fromEvent()创建的Observable上使用timeout()来执行此操作,但超时会返回两个Observable。

下面,如果在1秒内触发mousedown,订阅流将返回事件,但它也会返回1.

var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var stream = mousedown.timeout(1000, Rx.Observable.return(1));

var sub = stream.subscribe(
    function (x) { 
        console.log('Next: '+x);
    },
    function (err) {
        console.log('Err: '+err);
    },
    function () {
        console.log('Complete');
    }
);

但是,这可以按预期工作:

var source = Rx.Observable.return(42)
    .delay(200)
    .timeout(1000, Rx.Observable.return(1));

我希望这段代码能够运作:

var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var mouseup = Rx.Observable.fromEvent(target, 'mouseup');

var clickhold = mousedown
.flatMap(function (){
    return mouseup.timeout(1000, Rx.Observable.return('hold'));
})
.filter(function (x) {
    return x === 'hold';
});

clickhold.subscribe(
    function (x) { 
        console.log('Next: '+x);
    },
    function (err) {
        console.log('Err: '+err);
    },
    function () {
        console.log('Complete');
    }
);

2 个答案:

答案 0 :(得分:6)

我没有使用timeout,而是使用了delaytakeUntil



var target,
    mousedown,
    mouseup;

target = document.querySelector('input');

mousedown = Rx.Observable.fromEvent(target, 'mousedown');
mouseup = Rx.Observable.fromEvent(target, 'mouseup');

var clickhold = mousedown
    .flatMap(function(){
        // Triggered instantly after mousedown event.
        return Rx.Observable
            .return('hold')
            .delay(1000)
            // Discards the return value if by the time .delay() is complete
            // mouseup event has been already completed.
            .takeUntil(mouseup);
    });

clickhold.subscribe(
        function (x) { 
            console.log('Next: ' + x);
        },
        function (err) {
            console.log('Err: ' + err);
        },
        function () {
           console.log('Complete');
        }
    );

<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>

<input type='button' value='Button' />
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您自己想出了一个很好的解决方案。这就是我要改变的地方:

  1. 将内部可观察对象(timer(...).takeUntil(...).select(...))移出flatMap,因此不会为每个鼠标按下重新分配。
  2. 你已经完成了其余的工作。对于我的使用,我通常会保留原始的mousedown事件并使用该事件代替'hold'。这需要returnValuedelay,而不是timerselect

    &#13;
    &#13;
    var target,
        mousedown,
        mouseup;
    
    target = document.querySelector('input');
    
    mousedown = Rx.Observable.fromEvent(target, 'mousedown');
    mouseup = Rx.Observable.fromEvent(target, 'mouseup');
    
    var clickhold = mousedown
        .flatMap(function (e) {
            return Rx.Observable
                .return(e)
                .delay(1000)
                .takeUntil(mouseup);
        });
    
    clickhold.subscribe(function (x) { 
            console.log('onNext: ', x);
        });
    &#13;
    <script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
    
    <input type='button' value='Button' />
    &#13;
    &#13;
    &#13;

    或者,对于一种完全不同的方法......

    var Observable  = Rx.Observable,
        fromEvent   = Observable.fromEvent.bind(Observable, target),
        holdDelay   = Observable.empty().delay(1000);
    
    Observable
      .merge(
        [
          fromEvent('mouseup')
            .map(empty),
          fromEvent('mousedown')
            .map(Observable.returnValue)
            .map(holdDelay.concat.bind(holdDelay))
        ]
      )
      .switchLatest();
    

    好的,这很奇怪。我真的只是把它当作食物,并且要表明这可以通过多种不同的方式来完成。