[英]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.