djtorres
djtorres

Reputation: 153

How to detect when an observable sequence hasn't produced a value after some time

I have an observable that throttles dragover events by 350ms and returns true as long as those events keep coming in:

Rx.Observable
  .fromEvent(document, 'dragover')
  .map(function () { return true; })
  .throttle(350)
  .distinctUntilChanged()

I want to know if there hasn't been a dragover event withinin the last 350ms and return false when that happens. How should I go about doing that?

Do I need to have a separate timer and reset it whenever a dragover event occurs? I'm hoping that I can keep this stateless.

Upvotes: 2

Views: 1226

Answers (2)

paulpdaniels
paulpdaniels

Reputation: 18663

There is an operator called timeout that does this.

Rx.Observable
  .fromEvent(document, 'dragover')
  .throttle(350)
  .map(true)
  .timeout(350, Rx.Observable.just(false))
  .distinctUntilChanged();

A note that timeout will unsubscribe from the source Observable and subscribe with the one you passed in, so you won't get any further results from that Observable until your resubscribe to it.

Also I would recommend that you put the map operation after the throttle in order to avoid unnecessary mapping operations of values you won't use.

And I would suggest thinking about if those timings are really what you want, since you are throttling over 350ms, timing out on 350ms leaves some pretty narrow margins. 1/3 of a second is not a lot of time to react especially for users. Since you already have a downstream distinctUntilChanged you may not even need to throttle since you will only be emitting values when it goes from true to false.

Edit

If you need the Observable to continue you can use doWhile or repeat to resubscribe (I used mousemove for my tests):

Rx.Observable.fromEvent(window, 'mousemove')
 .map(true)
 .timeout(1000, Rx.Observable.just(false))
 .repeat()//.doWhile(function() { return true; })
 .distinctUntilChanged()
 .subscribe(function(value) { console.log(value); });

//Outputs
/* Mouse starts moving */
// true 

/* Mouse stopped moving for 1 second */
// false

/* Mouse still not moving no emit */

/* Mouse starts moving again */
// true

Upvotes: 3

djtorres
djtorres

Reputation: 153

Ok, I think I ended up solving my own question. I removed the throttle operator. It was redundant because the browser throttles the dragover event by 350ms anyway.

I created a source for dragover "start" events and a source for dragover "end" events. I gave both timestamps so that an end event's create time can be compared to a start event's create time:

var dragoverStart$ = Rx.Observable
  .fromEvent(this.dom, 'dragover')
  .timestamp()
  .pluck('timestamp');

var dragoverEnd$ = dragoverStart$
  .delay(350);

Rx.Observable
  .combineLatest(dragoverStart$, dragoverEnd$)
  .map(function (timestamps) {
    return timestamps[0] > timestamps[1];
  })
  .distinctUntilChanged()
  .subscribe(function (isDragging) {
    // ...
  });

Upvotes: 0

Related Questions