takeUntil не удается предотвратить выбросы из observable

#rxjs #rxjs-pipeable-operators

#rxjs #rxjs-pipeable-operators

Вопрос:

Я пытаюсь создать свои собственные события щелчка, удержания и перетаскивания, используя Rxjs и события mousedown, mouseup и mousemove. В моих попытках используется несколько потоков, которые начинаются с события наведения курсора мыши, каждый с takeUntil, который прослушивает выбросы из других потоков. В принципе, как только один из потоков «заявил» действие (т. Е. передал все требования и выдал значение), другие наблюдаемые объекты должны завершиться без выбросов.

Я просмотрел другие ответы и подумал, что это может иметь какое-то отношение к асинхронному запуску таймера, но это происходит между потоками, которые не полагаются на таймер, например, перетаскивание и щелчок. Я играл в codesandbox.io использование rxjs версии 6.

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

Код показан ниже:

 const mouse_Down$ = fromEvent(document, "mousedown").pipe(
  tap(event => event.preventDefault())
);

const mouse_Up$ = fromEvent(document, "mouseup").pipe(
  tap(event => event.preventDefault())
);

const mouse_Move$ = fromEvent(document, "mousemove");

const mouse_drag$ = mouse_Down$
  .pipe(
    mergeMap(mouseDownEvent =>
      mouse_Move$.pipe(takeUntil(merge(mouse_Up$, mouse_Hold$, mouse_drag$)))
    )
  ).subscribe(event => console.log("Drag"));

const mouse_Hold$ = mouse_Down$
  .pipe(
    mergeMap(mouseDownEvent =>
      timer(1000).pipe(takeUntil(merge(mouse_drag$, mouse_Click$)))
    )
  ).subscribe(event => console.log("Hold"));

const mouse_Click$ = mouse_Down$
  .pipe(
    mergeMap(mouseDownEvent =>
      mouse_Up$.pipe(takeUntil(mouse_drag$, mouse_Hold$))
    )
  ).subscribe(event => console.log("Click"));
  

Ожидаемое поведение:
Если пользователь перемещает мышь в течение 1 секунды после события mousedown, mouse_drag$ поток должен начать излучать, и mouse_Click$/mouse_Hold$ внутренние наблюдаемые объекты должны завершиться (благодаря takeUntil(mouse_drag$) без излучения и ожидания следующего mouse_down$ излучения.

Если кнопка мыши остается нажатой более 1 секунды без перемещения, mouse_Hold$ должна произойти эмиссия, а mouse_drag$/mouse_click$ внутренняя наблюдаемая должна завершиться (благодаря takeUntil(mouse_Hold$) без эмиссии и ожидания следующей mouse_down$ эмиссии.

Фактическое поведение: В данный момент mouse_Drag$ будет выделяться, mouse_Hold$ будет выделяться через одну секунду и mouse_Click$ будет выделяться при отпускании кнопки.

Мой вопрос в том, почему испускающий mouse_Drag$ поток не приводит к завершению внутренней observable mouse_Hold$ и mouse_Click$ без испускания?

Ответ №1:

Take until должно быть в конце вашей цепочки

Это отменит всю цепочку.

 const { fromEvent } = rxjs;
const { tap, takeUntil, mergeMap, merge } = rxjs.operators;

const mouse_Down$ = fromEvent(document, "mousedown").pipe(
  tap(event => event.preventDefault())
);

const mouse_Up$ = fromEvent(document, "mouseup").pipe(
  tap(event => event.preventDefault())
);

const mouse_Move$ = fromEvent(document, "mousemove");

const mouse_drag$ = mouse_Down$
  .pipe(
    mergeMap(mouseDownEvent =>
      mouse_Move$
    ),
    takeUntil(mouse_Up$)
  ).subscribe(event => console.log("Drag"));  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>  

Комментарии:

1. Спасибо за ответ, Адриан. Проблема с переносом takeUntil во внешнюю наблюдаемую заключается в том, что она завершит это наблюдаемое, и никакие будущие события mouse_drag $ не генерируются. т. Е. вы можете перетащить только один раз, и тогда никакие другие перетаскивания не будут зарегистрированы.

2. На самом деле, если вы хотите завершить mouse_Hold $ и mouse_Click $ — тогда они перестанут выделять — это ожидается. Или вы хотите их воссоздать? Из сделать один выброс результата наблюдаемым, если мышь перетаскивается более 1 секунды. А затем повторить эту логику?

Ответ №2:

Для уточнения:

  1. которые вы хотите выбросить из mouse_Hold $, если мышь удерживается более 1 секунды.
  2. Вы хотите получить значения из mouse_drag $, если менее чем через 1 секунду после выпадающего списка мыши и перемещения мыши.

Вам не нужно ничего завершать, поскольку в противном случае все поведение сработает только один раз. Итак, планируйте: 3. mouse_drag $ — Если mousedown — установите флажок mouseMove на 1 секунду. Если mouseMove выдает — переключитесь на значения mouseMove 4. mouse_Hold$ — если mouseDown — проверьте mouseMove на 1 секунду. Если mouseMove не выдает — переключитесь на mouseHold и заставьте его выдавать ‘Hold’

 let Rx = window['rxjs'];
const {defer, of, timer, fromEvent, merge, race} = Rx;
const {switchMap, repeat, tap, takeUntil, filter} = Rx.operators;
const {ajax} = Rx.ajax;
console.clear();

const mouse_Down$ = fromEvent(document, "mousedown");

const mouse_Up$ = fromEvent(document, "mouseup");

const mouse_Move$ = fromEvent(document, "mousemove");

const timer$ = timer(2000);

mouse_Hold$ = mouse_Down$.pipe(
  switchMap((downEvent) => {
    return timer$.pipe(
      switchMap((time) => of('HOLD'))
    );
  }),
  takeUntil(merge(mouse_Up$, mouse_Move$)),
  repeat(mouse_Down$)
)

mouse_Hold$.subscribe(console.warn);

mouse_drags$ = mouse_Down$.pipe(
  switchMap(() => mouse_Move$),
    takeUntil(mouse_Up$, $mouse_Hold),
  repeat(mouse_Down$)
)

mouse_drags$.subscribe(console.log);
  

Вот codepen:https://codepen.io/kievsash/pen/oOmMwp?editors=0010