繁体   English   中英

在 RxJS 中,是否应该在不发出任何项目的可观察对象中调用 complete?

[英]In RxJS, should complete be called in an observable that emits no items?

在我的上一篇文章中,我尝试使用 RxJS 缓冲待处理的 http 请求。 我认为bufferCount是我需要的,但我发现我的项目低于缓冲区大小,它只会等待,这不是我想要的。

我现在有一个新方案,使用take 它似乎在做我所追求的,除非我得到的可观察对象没有项目(左),否则永远不会调用完整的。

例如,我有类似以下内容..

      const pendingRequests = this.store$.select(mySelects.getPendingRequests).pipe(
      // FlatMap turns the observable of a single Requests[] to observable of Requests
      flatMap(x => x),

      // Only get requests unprocessed
      filter(x => x.processedState === ProcessedState.unprocessed),

      // Batches of batchSize in each emit
      take(3),
    );
    
    let requestsSent = false;
    pendingRequests.subscribe(nextRequest => {           
      requestsSent = true;
      this.sendRequest(nextEvent);           
     },
      error => {            
        this.logger.error(`${this.moduleName}.sendRequest:  Error ${error}`);
      },
      () => {         
        // ****  This is not called if pendingRequests is empty ****
        if (requestsSent ) {              
          this.store$.dispatch(myActions.continuePolling());              
        } else {
          this.store$.dispatch(myActions.stopPolling());              
        }
      }
    );

因此take(3)将获取接下来的 3 个待处理请求并将它们发送 () 我还调度一个操作以将已处理的 state 设置为 not ProcessedState.pending 所以我们不会在下一次轮询中得到它们

这一切都很好,但是当pendingRequests最终什么都不返回(为空)时, completed的块用 **** 标记。 不叫。 我原以为这会立即被调用。

我不确定这是否重要,因为我没有发送操作以继续轮询,因此轮询确实停止了。

但我最担心的是,如果pendingRequests没有完成,我是否需要取消订阅以防止任何泄漏? 我假设如果调用complete ,我不需要取消订阅?

更新

为了让pendingReguests总是完成,我采取了一种稍微不同的方法。 我没有使用rx运算符来“过滤”,而是每次都获取整个列表,然后对其take(1) 我总是会得到列表,即使它是空的,所以pendingReguests每次都会完成。

IE

const pendingRequests = this.store$.select(mySelects.getPendingRequests).pipe(take(1))

然后我可以在 observable 中过滤和批处理..

 pendingRequests.subscribe(nextRequest => {
      let requestToSend = nextRequest.filter(x => x.processedState === ProcessedState.unprocessed);
      const totalPendingCount = requestToSend.length;
      requestToSend = requestToSend slice(0, this.batchSize);          
      for (const nextRequest of requestToSend) {                  
          this.sendRequest(nextRequest);            
      }

      if (totalPendingCount > this.batchSize) {
        this.store$.dispatch(myActions.continuePolling());            
      }

到目前为止,在我的测试中,我现在总是可以complete开火。

此外,通过 2 个操作(一个 startPolling 和一个 continuePolling),我可以将延迟放在 continuePolling 中,所以当我们第一次开始轮询时(例如,应用程序在超出网络范围后刚刚恢复在线),我们立即提交,只有在我们有超过批量大小时才会延迟

也许这不是 100% 的“rxy”方式,但到目前为止似乎有效。 这里有什么问题吗?

之后我会用toArray和一些缓冲逻辑代替take

这就是您的代码的样子。 我添加了delay逻辑,我认为这是您之前的帖子所建议的,并提供了评论来描述添加的每一行

// implementation of the chunk function used below
// https://www.w3resource.com/javascript-exercises/fundamental/javascript-fundamental-exercise-265.php
   const chunk = (arr, size) =>
      Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
        arr.slice(i * size, i * size + size)
      );

  const pendingRequests = this.store$.select(mySelects.getPendingRequests).pipe(
  // FlatMap turns the observable of a single Requests[] to observable of Requests
  flatMap(x => x),

  // Only get requests unprocessed
  filter(x => x.processedState === ProcessedState.unprocessed),

  // Read all the requests and store them in an array
  toArray(),
  // Split the array in chunks of the specified size, in this case 3
  map(arr => chunk(arr, 3)), // the implementation of chunk is provided above
  // Create a stream of chunks
  concatMap((chunks) => from(chunks)),
  // make sure each chunk is emitted after a certain delay, e.g. 2 sec
  concatMap((chunk) => of(chunk).pipe(delay(2000))),
  // mergeMap to turn an array into a stream
  mergeMap((val) => val)
);

let requestsSent = false;
pendingRequests.subscribe(nextRequest => {           
  requestsSent = true;
  this.sendRequest(nextEvent);           
 },
  error => {            
    this.logger.error(`${this.moduleName}.sendRequest:  Error ${error}`);
  },
  () => {         
    // ****  THIS NOW SHOULD BE CALLED ****
    if (requestsSent ) {              
      this.store$.dispatch(myActions.continuePolling());              
    } else {
      this.store$.dispatch(myActions.stopPolling());              
    }
  }
);

我怀疑pendingRequests是否会自行完成。 Store至少在 ngrx 中是一个BehaviorSubject 因此,每当您执行store.select()store.pipe(select())时,您只是将另一个订阅者添加到由BehaviorSubject维护的订阅者的内部列表中。

BehaviorSubject 扩展了Subject ,以下是订阅Subject时发生的情况:

this.observers.push(subscriber);

在您的情况下,您正在使用take(3) 在 3 个值之后, take将发出一个完成通知,因此应该调用您的complete回调。 并且因为整个链实际上是一个BehaviorSubject的订阅者,它会在complete通知时将自己从订阅者列表中删除。

我假设如果调用完整,我不需要取消订阅

以下是订阅者(例如TakeSubscriber完成时发生的情况:

  protected _complete(): void {
    this.destination.complete();
    this.unsubscribe();
  }

因此,如果已经发生complete / error通知,则无需取消订阅。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM