使用切换 start/stop 按钮停止 API 调用 Rxjs

stop API calls using a toggle start/stop button Rxjs

我一直在学习 Rxjs,并创建了一个小幻灯片,在单击开始按钮后每 5 秒刷新一次图像。再次单击按钮后,我如何才能将 API 变为 stop/pause?

import './style.css';
import { tap, switchMap } from 'rxjs/operators';
import { fromEvent, Observable, timer } from 'rxjs';

function updateImages(
  links: string[]
  // link1: string,
  // link2: string,
  // link3: string
): void {
  document.getElementById('slideshow').childNodes.forEach((node: ChildNode) => {
    if (node.nodeType == Node.ELEMENT_NODE) {
      if (links.length) {
        let element: HTMLElement = node as HTMLElement;

        element.classList.add('loading');
        element.style.backgroundImage = "url('" + links.shift() + "')";
        element.classList.remove('loading');
      }
    }
  });
}

//api returns message w jpeg url & status
const apiUrl: string = 'https://dog.ceo/api/breeds/image/random';

const btn = document.getElementById('btn'); // get the button element

const btnEvents$ = fromEvent(btn, 'click');

const sub = btnEvents$.subscribe((result) => {
  startPolling('dogs').subscribe((dogs) => {
    updateImages(dogs.map(({ message }) => message));
  });
  btn.innerHTML = 'Stop';
  console.log(btn.classList);
});

function requestData(url: string): Observable<Array<{ message: string }>> {
  return Observable.create((observer) => {
    Promise.all([
      fetch(url).then((response) => response.json()),
      fetch(url).then((response) => response.json()),
      fetch(url).then((response) => response.json()),
      fetch(url).then((response) => response.json()),
    ])
      .then((dogs) => {
        console.log(dogs);
        observer.next(dogs);
        observer.complete();
      })
      .catch((err) => observer.error(err));
  }).pipe(tap((data) => console.log('dogrequest', data)));
}
function startPolling(
  category: string,
  interval: number = 5000
): Observable<Array<{ message: string }>> {
  const url = category === 'dogs' ? apiUrl : null;
  console.log(url);
  return timer(0, interval).pipe(switchMap((_) => requestData(url)));
}

目前它一直在获取数据,我无法让它停止。

如果我理解你想要实现的目标,你有一个按钮,它的行为就像一个开关,在某种意义上,当它被点击时它开始幻灯片放映(打开)并且当它再次被点击时它停止幻灯片(关闭)。

如果我的理解是正确的,这是使用 switchMap 运算符的一个很好的例子。

让我们看看如何实现。注释解释代码

// fist of all we define a variable holding the state of the switch
// there are more 'rxJs idiomatic' ways to manage the state, but for the moment
// lets stay with a variable holding the state
let on = false;

// this is your code that defines the btnEvents$ Observable which is the 
// starting point of our stream
const btn = document.getElementById('btn'); // get the button element
const btnEvents$ = fromEvent(btn, 'click');

// this is the core stream that implements our solution
// it starts with the stream of 'click' events
btnEvents$
  .pipe(
    // the tap operator allows us to implement side effects
    // in this particular case every time a click is notified we need to 
    // change the state (switch on/off) and set the label of the button
    tap(() => {
      on = !on;
      btn.innerHTML = on ? 'Stop' : 'Start';
    }),
    // here is where the switch happens
    // every time a click is notified by upstream, the switchMap operator
    // unsubscribes the previous Observable and subscribes to a new one
    switchMap(() => {
      // this means that when a new click event is notified by upstream
      // we check the state
      // if the state is "on" we return the stream that fetches the data
      // otherwise we return an Observable that notifies just an empty array
      return on ? requestData("dogsUrl") : of([]);
    }),
    // finally last side effect, i.e. we update the images based on the 
    // arrays of dogs returned
    // consider that if the state is "off" than the "dogs" array is empty
    // and therefore no update is performed
    tap(dogs => updateImages(dogs.map(({ message }) => message));)
  )
  // now that we have built the Observable as per our requirements, 
  // we just have to subscribe to it to trigger the entire execution
  .subscribe(console.log);

实现不完整,例如缺少错误处理逻辑,但我希望足以阐明思路