下面:
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家伙如何解决它的?
答案 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.