bnieland
bnieland

Reputation: 6516

How to compose a sequence of observables in rxjs

I am using rxjs to handle some complex mouse gestures.

One of these gesures is to "shake" the mouse (left to right, right to left, ...) several times during a drag operation.

As I do not know whether the user will start by moving the the left or right, I am using 'race()' to accept whichever starts first.

My problem is that when the first "shake" observer completes, the second has already completed.

I have made a simplified example here, on jsbin (http://jsbin.com/wuzodog/3/edit?html,js,output.)

(I am using negative numbers as "mouse left" and positive as "mouse right" in this simplified example.)

The core of the issue is demonstrated here...

  const subMouseMove = new Rx.Subject<number>();

  const obLeft = subMouseMove.filter(m => m < 0);
  const obRight = subMouseMove.filter(m => m >= 0);

  const get_obLeftUntilRight = () => obLeft.takeUntil(obRight);
  const get_obRightUntilLeft = () => obRight.takeUntil(obLeft);

  const get_obShake = () => Rx.Observable.race(get_obLeftUntilRight(), get_obRightUntilLeft()); 

  const obDragShakeAndDrop = Rx.Observable.concat(
    get_obShake(),
    get_obShake(),
  );

  //sending the following values to subMouseMove 
  [-1, -2, 3, 4, -5].forEach((v, i, a) => setTimeout(() => subMouseMove.next(v), i * 10));

The problem is that both instances of get_obShake() are being completed at the same time.

The results I am getting from the jsbin are:

Actual Results

I would expect my results to be:

Expected results

When I modify the example (http://jsbin.com/wuzodog/4/edit?html,js,output), calling the "shake left/right" and the "shake right/left" directly, bypassing "race()", I get the 'expected' output.

Can anyone explain why the second shake is completing early in the first example?

Upvotes: 0

Views: 889

Answers (2)

ZahiC
ZahiC

Reputation: 14717

It's tricky, but the answer is more rational then you think. The issue is that Race also accepts completion as the first winning Observable.

const get_obLeftUntilRight = () => obLeft.takeUntil(obRight);
const get_obRightUntilLeft = () => obRight.takeUntil(obLeft);

When we look at these two Observables, if there is 'left' event, the first will emit and the second will complete immediately. If we get a 'right' event the first will complete immediately and the second...

This is probably what you want:

const get_obLeftUntilRight = () => obLeft.take(1).concat(obLeft.takeUntil(obRight));
const get_obRightUntilLeft = () => obRight.take(1).concat(obRight.takeUntil(obLeft));

Upvotes: 2

Richard Matsen
Richard Matsen

Reputation: 23533

The difference between the two jsBin outputs is that

** NEXT: get_obShake2 = 4

is missing from the first, because race has eliminated it.

So, it's not that the second shake is completing early, it's just a shorter sequence.

Add some timestamp() to check it out.

I have to say, I don't get the feeling of this code doing what you intend, but that could well be my lack of understanding.

Upvotes: -1

Related Questions