重用数据流一段时间

时间:2017-06-02 11:22:47

标签: 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可以被包装并且被拦截。

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);
  }
}

但是如何使用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;
    }
  }
}

可以用更多声明性的rx-way实现同样的目标吗?

1 个答案:

答案 0 :(得分:0)

我没有测试过,但是我这样做了:

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;
            }
        });
}

基本上唯一的区别是我在this.activeQueries中存储了Observable而不仅仅是他们的结果,然后我使用.publishReplay(1, 5000)将它们缓存5秒。这样,当您在5s之后订阅相同的Observable时,它只会重新订阅其源Observable。