【问题标题】:Can't figure out takeUntil behavior无法弄清楚 takeUntil 行为
【发布时间】:2021-07-29 07:25:06
【问题描述】:

我不知道用takeUntil 停止流媒体有多合适。

间隔必须在第一次、第三次等点击按钮时开始发射,并在每第二次、第四次等点击后停止发射。

UPD:此代码的目的是提供一种“秒表”。第一次点击时,它应该从 0 开始并计数 1、2 等。在其他点击时,它应该开始/停止计数。

const justIndex = (_, i) => i;
const isOdd = (x) => x % 2;
const isEven = (x) => !isOdd(x);

const buttonClick$ = fromEvent(buttonEl, "click").pipe(map(justIndex));
const resumeClick$ = buttonClick$.pipe(filter(isEven));
const pauseClick$ = buttonClick$.pipe(filter(isOdd));

const intervalFrom = () => 
  interval(500).pipe(
    takeUntil(pauseClick$), // wtf this is not working
    // takeUntil(resumeClick$), // but this is
  );

const resumeInterval$ = resumeClick$.pipe(
  switchMap(intervalFrom),
);

This code at codesandbox.io

【问题讨论】:

    标签: javascript rxjs


    【解决方案1】:

    问题在于您没有共享订阅fromEvent(buttonEl, "click"),这意味着每次订阅buttonClick$resumeClick$pauseClick$ 时,您都在创建一个新的“点击”侦听器。反过来,这意味着当intervalFrom 被调用时,takeUntil(pauseClick$) 会重新订阅pauseClick$,从而使用始终从0 开始的fromEvent 创建一个新的事件侦听器。

    所以您要做的是共享一个使用fromEvent 创建的事件侦听器,以便在您的 RxJS 链中的任何地方共享点击索引计数器:

    const buttonClick$ = fromEvent(buttonEl, "click").pipe(map(justIndex), share());
    

    您更新的演示:https://codesandbox.io/s/rxjs-stopwatch-problem-forked-4w83h?file=/src/index.js

    【讨论】:

      【解决方案2】:

      我测试了您的代码沙盒。我没有解决方案,但我观察到一些可能非常重要的事情。 fromEvent observable 在单击按钮时不仅会发出一个事件,而且每次都会发出多个事件。这可能会一开始就搞砸你的整个设置。

      试试这个来验证:

      const buttonClick$ = fromEvent(buttonEl, "click").pipe(
        tap(n => console.log(n))
        );
      

      【讨论】:

      • 谢谢。这是fromEvent 的预期行为吗?
      • 我不知道。我想是的,但我同意这有点不直观。
      【解决方案3】:

      您没有提供太多信息,因为您想要达到的什么,所以我做了一些假设,希望这些假设是您想要的,或者至少足够接近,您可以弄清楚缺少什么。

      
      const TIME_INTERVAL = 500;
      const COUNTER_INITIALLY_ON = false;
      
      const DOM = {
        counter: document.getElementById('counter') as HTMLDivElement,
        toggleButton: document.getElementById('toggleButton')
      } as const;
      
      const toggleButtonClick$ = fromEvent(DOM.toggleButton, 'click');
      
      const isCounterOn$ = toggleButtonClick$.pipe(
        scan(isCounterOn => !isCounterOn, COUNTER_INITIALLY_ON),
        startWith(COUNTER_INITIALLY_ON)
      );
      
      const counter$ = isCounterOn$.pipe(
        switchMap(isCounterOn => {
          if (!isCounterOn) {
            return EMPTY;
          }
      
          return timer(0, TIME_INTERVAL).pipe(map(x => x * TIME_INTERVAL));
        })
      );
      
      const updateDomSideEffect$ = counter$.pipe(
        tap(counter => {
          // side effects are run in a `tap` and are completely isolated
          // from the rest of the streams
          DOM.counter.textContent = counter;
        })
      );
      
      updateDomSideEffect$.subscribe();
      

      这是一个实时版本的堆栈闪电战:https://stackblitz.com/edit/rxjs-pniaae?devtoolsheight=60&file=index.ts

      PS:我认为代码描述性足够,我不需要解释太多,但如果有任何不清楚的地方,请告诉我,我可以提供更多详细信息

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多