通过流切换API调用

时间:2017-05-29 10:53:31

标签: javascript rxjs reactive-programming reactivex

下面:

import Rx from 'rxjs';

function fakeApi(name, delay, response) {
  return new Rx.Observable(observer => {
    console.log(`${name}: Request.`)
    let running = true;
    const id = setTimeout(() => {
      console.log(`${name}: Response.`)
      running = false;
      observer.next(response);
      observer.complete();
    }, delay);
    return () => {
      if(running) console.log(`${name}: Cancel.`)
      clearTimeout(id);
    }
  })
}

function apiSearch() { return fakeApi('Search', 4000, "This is a result of the search."); }


//============================================================

const messages$ = new Rx.Subject();

const toggle$ = messages$.filter(m => m === 'toggle');

const searchDone$ = toggle$.flatMap(() => 
  apiSearch().takeUntil(toggle$)
);

searchDone$.subscribe(m => console.log('Subscriber:', m))

setTimeout(() => {
  // This one starts the API call.
  toggle$.next('toggle'); 
}, 2000)

setTimeout(() => {
  // This one should only cancel the API call in progress, not to start a new one.
  toggle$.next('toggle'); 
}, 3000)

setTimeout(() => {
  // And this should start a new request again...
  toggle$.next('toggle'); 
}, 9000)

我的目的是启动API调用,并在相同的toggle$信号进行时停止它。代码问题是toggle$每次都会启动一个新的API调用。我希望它不会在已经运行的时候启动新的呼叫,只是为了停止正在进行的呼叫。我应该采取某种方式取消订阅"当flatMap正在运行时,来自toggle$流的最外层apiSearch()。我想有必要重构代码以实现行为...... RxJS的做法是什么?

更新:在进行了一些调查和用户指南查询之后,我带来了这个:

const searchDone$ = toggle$.take(1).flatMap(() =>
  apiSearch().takeUntil(toggle$)
).repeat()

应该这样工作。仍然感到神秘一点点。这是你RxJS家伙如何解决它的?

1 个答案:

答案 0 :(得分:0)

我认为您的解决方案只能使用take(1)一次。你可以这样做:

const searchDone$ = toggle$
    .let(observable => {
        let pending;

        return observable
            .switchMap(() => {
                let innerObs;

                if (pending) {
                    innerObs = Observable.empty();
                } else {
                    pending = innerObs = apiSearch();
                }

                return innerObs.finally(() => pending = null);
            });
    });

我只使用let()来包装pending而不在父范围内声明它。 switchMap()运算符会在不使用take*()的情况下自动取消订阅。

测试setTimeout的输出如下:

Search: Request.
Search: Cancel.
Search: Request.
Search: Response.
Subscriber: This is a result of the search.