根据反应式编程启动和停止计数器

时间:2019-07-01 23:11:18

标签: javascript rxjs reactive-programming

我有两个按钮,一个按钮初始化一个计数器,另一个停止计数器。 按照声明式编程,我根据命令式编程实现了一个函数,并根据可观察性实现了另一个函数。

但是,我对声明式代码不满意,因为它具有副作用和全局参数。

是否有一种方法可以根据功能性反应式编程实现无副作用和全局参数的功能?

function exercise_pause_play_observable__imperative() {
    const btnStart = document.querySelector('#btnStart');
    const btnStop = document.querySelector('#btnStop');
    const timer1 = document.querySelector('#timer1') as HTMLDivElement;

    let isEnabled = false;
    let cant = 0;
    let int1: number;
    btnStart.addEventListener('click', () => {
        if (!isEnabled) {
            isEnabled = true;
            int1 = setInterval(() => {
                cant++;
                timer1.textContent = cant.toString();
            }, 1000)
        }

    });
    btnStop.addEventListener('click', () => {
        isEnabled = false;
        clearInterval(int1);
    });
}

function exercise_pause_play_observable__declarative()  {
    const btnStart = document.querySelector('#btnStart');
    const btnStop = document.querySelector('#btnStop');
    const timer1 = document.querySelector('#timer1') as HTMLDivElement;

    let isEnabled = false;
    let cant = 0;
    let int1: number;

    const clickStart$ = fromEvent(btnStart, 'click').pipe(
        tap(e => {
            if (!isEnabled) {
                isEnabled = true;
                int1 = setInterval(() => {
                    cant++;
                    timer1.textContent = cant.toString();
                }, 1000);
            }
        })
    );
    const clickStop$ = fromEvent(btnStop, 'click').pipe(
        tap(e => {
            isEnabled = false;
            clearInterval(int1);
        })
    );

    merge(clickStart$, clickStop$);
}

HTML

<body>
    <button id="btnStart">START</button>
    <button id="btnStop">STOP</button>
    <div id="timer1"></div>


    <script src="/bundle.js"></script>
</body>

3 个答案:

答案 0 :(得分:0)

使用一个名为stop $的主题通过takeUntil取消的时间间隔。

const { Subject, fromEvent, interval } = rxjs;
const { takeUntil } = rxjs.operators;

const stop$ = new Subject();

fromEvent(document.getElementById('btnStart'), 'click').subscribe(() => {
  stop$.next(); // If restarting make sure the old one is cancelled
  const timerDiv = document.getElementById('timer1');
  timerDiv.innerText = '';
  interval(1000).pipe(takeUntil(stop$)).subscribe(val => {
    timerDiv.innerText = (val + 1);
  });
});

fromEvent(document.getElementById('btnStop'), 'click').subscribe(() => {
  stop$.next();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>

<button id="btnStart">START</button>
<button id="btnStop">STOP</button>
<div id="timer1"></div>

答案 1 :(得分:0)

该如何实现:

用户单击“开始”按钮后立即开始间隔,然后单击停止按钮将发出NEVER。还使用scan运算符跟踪最后发出的值。让我们在rxjs中将此语句编码如下:

const btnStart = document.querySelector('#btnStart');
const btnStop = document.querySelector('#btnStop');
const timer1 = document.querySelector('#timer1') as HTMLDivElement;

//button start observable maps to true
const btnStart$ = fromEvent(btnStart, 'click').pipe(mapTo(true));

//button stop observable maps to false
const btnStop$ = fromEvent(btnStop, 'click').pipe(mapTo(false));

merge(btnStart$, btnStop$)
.pipe(
  switchMap(isStart => {
    return isStart ? interval(500) : NEVER;
  }),
  //this will start your interval from where you last stopped
  //i.e. keep track of last emission when stop was clicked
  scan(acc => ++acc),
  tap(interval => {
    timer1.textContent = `${interval + 1}`;
  }),
).subscribe();

正在工作的Stackblitz-https://stackblitz.com/edit/typescript-uywbc6?file=index.html

请注意,我们不会保存任何临时状态来跟踪最近的排放。我们正在使用scan运算符来完成[即让rxjs为我们做到这一点]。

答案 2 :(得分:0)

这应该可以实现您想要的。将计数存储在defer()scan中。我可能想到的最简单的形式。

const start$=fromEvent(document.querySelector('#start'),'click')
const stop$=fromEvent(document.querySelector('#stop'),'click')
    defer(()=>{
        let count=0;
        return interval(1000).pipe(
          windowToggle(start$,()=>stop$),
          exhaustMap(obs=>obs),
          map(_=>count++)
        )
    }).subscribe(console.log)

    // or
     interval(1000).pipe(
      windowToggle(start$,()=>stop$),
      exhaustMap(obs=>obs),
      scan((acc,curr)=>++acc,0)
    ).subscribe(console.log)