承诺可观察到的节点js循环

时间:2020-08-26 21:47:11

标签: node.js typescript async-await rxjs observable

我有一种方法,可以对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();

2 个答案:

答案 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)

https://stackblitz.com/edit/rxjs-ihxkax?file=index.ts