Повторное использование потоков данных в течение некоторого времени

1

Скажем, у одного есть 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 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?

Теги:
functional-programming
rxjs
reactive-programming
rxjs5

1 ответ

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 Observables в this.activeQueries а не только их результаты, а затем я .publishReplay(1, 5000) их на 5 с использованием .publishReplay(1, 5000). Таким образом, когда вы подписываетесь на тот же Observable после 5s, он будет просто повторно подписываться на свой источник Observable.

  • 0
    Если вы не используете flatMap, он возвращает поток более высокого порядка, поэтому нарушает контракт. В оригинальных примерах также сохранялись наблюдаемые (однако publishReplay лучше, чем publishLast) в activeQueries. Ссылка на документацию предлагает использовать Rx.Obervable.defer и фабрику потоков, чтобы убедиться, что вызовы API действительно выполняются.
  • 0
    Хотя было бы неплохо избавиться и от этой карты активных запросов.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню