Javascript 在一段时间内重复使用数据流

Javascript 在一段时间内重复使用数据流,javascript,functional-programming,rxjs,reactive-programming,rxjs5,Javascript,Functional Programming,Rxjs,Reactive Programming,Rxjs5,假设有一个API可以接受查询并返回结果流,因为某些结果可能会更改 type Query = { species?: "dog" | "cat" | "rat", name?: "string", status?: "lost" | "found" } type Result = { species: string, name: string, status: string }[] 假设有多个组件向这个API传递查询,其中一些可能是相同的。人们不想向服务器发送不必要的请求,而是喜

假设有一个API可以接受查询并返回结果流,因为某些结果可能会更改

type Query = { 
  species?: "dog" | "cat" | "rat", 
  name?: "string",
  status?: "lost" | "found"
}
type Result = { species: string, name: string, status: string }[]
假设有多个组件向这个API传递查询,其中一些可能是相同的。人们不想向服务器发送不必要的请求,而是喜欢优化——为了做到这一点,可以包装API并拦截调用

interface ServiceApi {
  request(query: Query): Observable<Result>
}

class WrappedServiceApi implements ServiceApi {
  constructor(private service: ServiceApi) { }

  request(query: Query): Observable<Result> {
    // intercepted
    return this.service.request(query);
  }
}
接口服务API{
请求(查询:查询):可观察
}
类WrappedServiceApi实现ServiceApi{
构造函数(专用服务:ServiceApi){}
请求(查询:查询):可观察{
//拦截
返回此.service.request(查询);
}
}
但如何使用RxJS 5实现此类优化?

围绕RxJS执行此操作可能类似于以下内容:

class WrappedServiceApi implements ServiceApi {

  private activeQueries;
  constructor(private service: ServiceApi) { 
    this.activeQueries = new Map<string, Observable<Result>>();
  }

  request(query: Query): Observable<Result> {
    // it's easy to stringify query
    const hashed: string = hash(query);
    if (this.activeQueries.has(hashed)) {
      // reuse existing stream
      return this.activeQueries.get(hashed);
    } else {
      // create multicast stream that remembers last value
      const results = this.service.request(query).publishLast();
      // store stream for reuse
      this.activeQueries.set(hashed, results);
      // delete stream 5s after it closed
      results.toPromise().then(
        () => setTimeout(
          () => this.activeQueries.delete(hashed), 
          5000
        )
      );
      return results;
    }
  }
}
类WrappedServiceApi实现ServiceApi{
私有查询;
构造函数(专用服务:ServiceApi){
this.activequerys=newmap();
}
请求(查询:查询):可观察{
//将查询字符串化很容易
常量散列:字符串=散列(查询);
if(this.activequerys.has(散列)){
//重用现有流
返回this.activequerys.get(散列);
}否则{
//创建记住最后一个值的多播流
const results=this.service.request(query.publishLast();
//存储流以供重用
this.activequerys.set(散列,结果);
//在流关闭后删除它
results.toPromise()。然后(
()=>设置超时(
()=>this.activequerys.delete(散列),
5000
)
);
返回结果;
}
}
}

可以通过更具声明性的接收方式实现同样的效果吗?

我没有测试它,但我会这样做:

request(query: Query): Observable<Result> {
    return Observable.of(hash(query))
        .flatMap(hashed => {
            const activeQueries = this.activeQueries;
            if (activeQueries.has(hashed)) {
                return activeQueries.get(hashed);
            } else {
                const obs = this.service.request(query)
                    .publishReplay(1, 5000)
                    .refCount()
                    .take(1);

                activeQueries.set(hashed, obs);
                return obs;
            }
        });
}
请求(查询:查询):可观察{
可观察的返回值(散列(查询))
.flatMap(散列=>{
const activequerys=this.activequerys;
if(activequerys.has(散列)){
返回activequerys.get(散列);
}否则{
const obs=this.service.request(查询)
.publishReplay(15000)
.refCount()
.采取(1)项措施;
activequerys.set(散列,obs);
返回obs;
}
});
}

基本上唯一的区别是我在
this.activequerys
中存储可观察到的内容,而不仅仅是它们的结果,然后使用
.publishReplay(15000)
将它们缓存5秒钟。这样,当您在5秒后订阅相同的Observable时,它将重新订阅其源Observable。

我没有测试它,但我会这样做:

request(query: Query): Observable<Result> {
    return Observable.of(hash(query))
        .flatMap(hashed => {
            const activeQueries = this.activeQueries;
            if (activeQueries.has(hashed)) {
                return activeQueries.get(hashed);
            } else {
                const obs = this.service.request(query)
                    .publishReplay(1, 5000)
                    .refCount()
                    .take(1);

                activeQueries.set(hashed, obs);
                return obs;
            }
        });
}
请求(查询:查询):可观察{
可观察的返回值(散列(查询))
.flatMap(散列=>{
const activequerys=this.activequerys;
if(activequerys.has(散列)){
返回activequerys.get(散列);
}否则{
const obs=this.service.request(查询)
.publishReplay(15000)
.refCount()
.采取(1)项措施;
activequerys.set(散列,obs);
返回obs;
}
});
}

基本上唯一的区别是我在
this.activequerys
中存储可观察到的内容,而不仅仅是它们的结果,然后使用
.publishReplay(15000)
将它们缓存5秒钟。这样,当您在5秒后订阅同一个Observable时,它只会重新订阅其源Observable。

除非您执行flatMap,否则它会返回更高阶的流,因此违反了合同。原始示例也在activeQueries中存储了可观测值(不过publishReplay比publishLast更好)。文档链接建议使用Rx.Obervable.defer和stream factory,以确保对API的调用是实际进行的。不过,最好也去掉这个activeQueries映射。没错,应该有
flatMap
,除非您使用flatMap,否则它会返回更高阶的流,因此会破坏契约。原始示例也在activeQueries中存储了可观测值(不过publishReplay比publishLast更好)。文档链接建议使用Rx.Obervable.defer和stream factory,以确保对API的调用是实际进行的。不过,最好也去掉这个activeQueries映射。的确,应该有
flatMap