我有一种方法,可以对DB进行许多异步查询以检索大量数据。为了简单起见,可以说每个查询返回一个整数数组。我想将此方法转换为可观察的并逐一输出数字。这部分工作正常。
问题始于'take'运算符-如果仍然没有人在听结果,我想停止数据库请求。我的问题是,“滚动”功能直到达到停止条件时才会停止执行,即使由于“ take(10)”运算符,“ largeQueryPromise”不再监听它。
当订户由于各种原因取消订阅时,是否有可能停止可观察的执行?
let ind = 0;
function dbRequest(): Promise<number[]> {
return new Promise(resolve => resolve([ind++, ind++]));
}
async function largeQuery(index: number) {
let res = await dbRequest();
return new Observable(observer => scroll(observer, res, index));
}
function scroll(observer: Subscriber<number>, res: number[], index: number) {
if (Math.round(Math.random() * 5) === 0) {
console.log(`completed sequence ${index}`);
observer.complete();
return;
}
res.forEach(value => observer.next(value));
dbRequest().then(arr => scroll(observer, arr, index));
}
async function largeQueryPromise(index: number) {
console.log(`started sequence ${index}`);
const obs = await largeQuery(index);
obs.pipe(take(10)).subscribe(
undefined,
console.error,
() => {
console.log(`stopped to listen for sequence ${index}`);
largeQueryPromise(++index).then();
});
}
largeQueryPromise(0).then();
答案 0 :(得分:1)
观察者具有一个参数“ closed”,指示该订阅者是否已取消订阅。知道这一解决方案很简单:
function scroll(observer: Subscriber<number>, res: number[], index: number) {
if (Math.round(Math.random() * 5) === 0) {
console.log(`completed sequence ${index}`);
observer.complete();
return;
}
for(let i=0; i<res.length && !observer.closed; i++)
observer.next(res[i]);
if(!observer.closed)
dbRequest().then(arr => scroll(observer, arr, index));
}
编辑:请注意,从技术上讲,您不需要在for循环中进行检查-所有.next都是noop。
答案 1 :(得分:1)
您的largeQuery只能由运算符完成。在上一个请求发出时,使用expand
递归调用dbRequest()
。通过返回EMPTY
结束此递归。使用concatAll
分散传入的阵列发射。
function largeQuery(index: number): Observable<number> {
console.log("largeQuery2 for", index);
return from(dbRequest()).pipe(
expand(res => {
if (Math.round(Math.random() * 5) === 0) {
console.log(`completed sequence ${index}`);
return EMPTY;
}
// The observable returned here gets subscribed to before the 'take' operator
// below ends the subscription. To prevent an additional call of 'dbRequest'
// at the end, the observable returned here has to be asynchronous.
// That's why 'timer' is used.
// If this doesn't turn out to be an issue for you, the line below could be
// replace with 'return defer(() => dbRequest())' or even 'return from(dbRequest())'
return timer(0).pipe(switchMap(() => dbRequest()));
}),
concatAll()
);
}
function recursiveLargeQuery(index: number) {
console.log(`started sequence ${index}`);
largeQuery(index).pipe(
take(10),
).subscribe(
v => console.log(v),
console.error,
() => {
console.log(`stopped to listen for sequence ${index}`);
if (index < 2) { // end the recursion at some point
recursiveLargeQuery(++index);
}
});
}
recursiveLargeQuery(0)