繁体   English   中英

RxJS - 如何分享昂贵的可观察量的输出但如果在N秒后再次请求则重新运行该观察值?

[英]RxJS - How to share the output of an expensive observable but rerun that observable if its requested again after N seconds?

假设我们有这个全局const:

const isSignedIn = fromPromise(fetch('/api/is-signed-in'))
    .pipe(throttleTime(1000), shareReply(1));

页面加载后,几个组件将同时订阅它:

isSignedIn.subscribe(() => console.log('do 1st'));
isSignedIn.subscribe(() => console.log('do 2nd'));
isSignedIn.subscribe(() => console.log('do 3rd'));

以上只会调用API一次,但是我需要它再次调用API(即1秒后),如果另一个组件订阅它。

isSignedIn.subscribe(() => console.log('button press'));

我如何使用RxJS?

编辑:答案是错的。 BufferSize是重播最后N个事件的时间。 在此之后,流完成。

signature: shareReplay(
  bufferSize?: number,
  windowTime?: number,
  scheduler?: IIScheduler
):Observable

@param {Number} [bufferSize=Number.POSITIVE_INFINITY] Maximum element count of the replay buffer.
@param {Number} [windowTime=Number.MAX_VALUE] Maximum time length of the replay buffer in milliseconds.

尝试将1000作为第二个参数添加到shareReply:

const isSignedIn = fromPromise(fetch('/api/is-signed-in'))
    .pipe(throttleTime(1000), shareReplay(1, 1000));

shareReplay.ts - 关注refCount--取消订阅,因为它可以触发其他请求。

如果我们重新实现ShareReplay,那么:
- 即使没有更多订阅者,也永远不会取消订阅源(删除refCount,潜在的内存泄漏)。
- 接受rerunAfter参数,从上次subscribe传递到源的时间。

import {Subject, of, Observable, ReplaySubject, Subscriber} from 'rxjs';
import {pluck, shareReplay, tap, delay} from 'rxjs/operators';

function shareForeverReplayRerun<T>(bufferSize: number, rerunAfter: number) {
  let subject;
  let subscription;
  let hasError = false;
  let isComplete = false;
  let lastSubTime = 0;

  return source => Observable.create((observer: Subscriber<T>) => {
    if (!subject || hasError || (Date.now() - lastSubTime) >= rerunAfter) {

      lastSubTime = Date.now();
      hasError = false;
      subject = new ReplaySubject<T>(bufferSize);
      subscription = source.subscribe({
        next(value) { subject.next(value); },
        error(err) {
          hasError = true;
          subject.error(err);
        },
        complete() {
          isComplete = true;
          subject.complete();
        },
      });
    }

    const innerSub = subject.subscribe(observer);
    // never unsubscribe from source
    return () => {
      innerSub.unsubscribe();
    };
  })
}





const source = of('Initial').pipe(
  tap(()=>console.log('COMPUTE')),
  delay(200),
  shareReplayRerun(1, 1000),
);

source.subscribe(console.log.bind(null, 'syncI:'));
source.subscribe(console.log.bind(null, 'syncII:'));

setTimeout(()=>source.subscribe(console.log.bind(null, 'after500:')), 500);

setTimeout(()=>source.subscribe(console.log.bind(null, 'after900:')), 900);

setTimeout(()=>source.subscribe(console.log.bind(null, 'after1500:')), 1500);

作为输出我们有:

COMPUTE
syncI:    Initial
syncII:   Initial
after500: Initial
after900: Initial
COMPUTE
after1500:Initial

我想这就是你想要的:

一个可管理的运算符(在某处全局声明并导入它)

 export const refreshAfter = (duration: number) => (source: Observable<any>) =>
                             source.pipe(
                                   repeatWhen(obs => obs.pipe(delay(duration))),
                                   publishReplay(1), 
                                   refCount());

然后像这样使用它:

data$ = fetch('/api/is-signed-in').pipe(refreshAfter(5000));   // refresh after 5000 ms

注意:您实际上是这样要求的:

我需要它再次调用API(即1秒后),如果另一个组件订阅它。

不太确定这是你真正的意思。 我认为你真正的意思是 - 你想要在到期时间之后订阅当前订阅的所有组件的数据。 无论如何,我的回答将新值发送给所有听众。 如果你真的想要你最初说的话,你需要添加某种替代重复触发器。

但如果这是一个全局常量 - 上面是我在同一场景中使用的。

注意:当项目重复时,我实际上没有测试错误条件的处理,但我认为错误将传播给所有侦听器。

暂无
暂无

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

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