Временное кэширование HTTP-ответов от параметризованных запросов с использованием RxJS (и Angular) - PullRequest
0 голосов
/ 01 марта 2019

Я бы хотел кешировать и завершить HTTP-ответы от, например, запроса GET с определенными параметрами.

Пример варианта использования:

Предположим, я строюсервис, подобный этому:

@Injectable()
export class ProductService {

  constructor(private http: HttpClient) {}

  getProduct$(id: string): Observable<Product> {
    return this.http.get<Product>(`${API_URL}/${id}`);
  }

  getProductA$(id: string): Observable<ProductA> {
    return this.getProduct$(id).pipe(
      // bunch of complicated operations here
    };
  }

  getProductB$(id: string): Observable<ProductB> {
    return this.getProduct$(id).pipe(
      // bunch of other complicated operations here
    };
  }

}

Теперь по какой-то причине функция A вызывается в компоненте A, а функция B вызывается в компоненте B. Я знаю, что это можно сделать другим способом (например, top-интеллектуальный компонент уровня получает данные HTTP и передает их через входные параметры), но по какой-то причине эти два компонента являются «интеллектуальными», и каждый из них вызывает сервисную функцию.

Оба компонента загружаются на одну и ту же страницу,поэтому происходят две подписки = два HTTP-запроса к одной и той же конечной точке - хотя мы знаем, что результат, скорее всего, будет одинаковым.

Я хочу просто кэшировать ответ getProduct$, НО я тоже хочу этосрок действия кэша истекает довольно быстро, потому что через 2 минуты Маргарет из управления продуктами собирается изменить цену продукта.

То, что я пробовал, но не работает:

По сути, я попытался сохранить словарь горячих наблюдаемых, используя shareReplay с временем окна 5 с.Я предположил, что если (исходная) наблюдаемая завершена или число подписок равно 0, то следующий подписчик просто перезапустит наблюдаемое, но, похоже, это не так.

private product$: { [productId: string]: Observable<Product> } = {};

getProduct$(id: string): Observable<Product> {
  if (this.product$[id]) {
    return this.product$[id];
  }
  this.product$[id] = this.http.get<Product>(`${API_URL}/${id}`)
    .pipe(
      shareReplay(1, 5000), // expire after 5s
    )
  );
  return this.product$[id];
}

Я думалЯ могу попытаться удалить наблюдаемое из моего словаря по завершении, используя finalize или finally, но, к сожалению, они также вызываются при каждой отмене подписки.

Таким образом, решение должно быть, возможно, более сложным.

Любоепредложения?

1 Ответ

0 голосов
/ 02 марта 2019

Если я правильно вас понимаю, вы хотите кэшировать ответы на основе их параметра id, поэтому, когда я сделаю два getProduct() с разными id с, я получу два разных некэшированных результата.

Iдумаю, что последний вариант почти правильный.Вы хотите, чтобы он отписался от своего родителя, чтобы потом он мог повторно подписаться и обновить кэшированное значение.

Оператор shareReplay работал по-разному до RxJS 5.5, если я правильно помню это, где shareReplay изменилось и этоне переподписывался на его источник.Позже это было переопределено в RxJS 6.4, где вы можете изменить его поведение на основе объекта конфигурации, переданного в shareReplay.Поскольку вы используете shareReplay(1, 5000), похоже, что вы используете RxJS <6.4, поэтому вместо них лучше использовать операторы <code>publishReplay() и refCount().

private cache: Observable<Product>[] = {}

getProduct$(id: string): Observable<Product> {
  if (!this.cache[id]) {
    this.cache[id] = this.http.get<Product>(`${API_URL}/${id}`).pipe(
      publishReplay(1, 5000),
      refCount(),
      take(1),
    );
  }

  return this.cache[id];
}

Обратите внимание, что я также включил take(1),Это потому, что я хочу, чтобы цепочка завершалась сразу после того, как publishReplay испустит свой буфер и до того, как он подпишется на свой источник Observable.Нет необходимости подписываться на его источник, потому что мы просто хотим использовать кэшированное значение.Через 5 с кэшированное значение отбрасывается, и publishReplay снова подпишется на его источник.

Надеюсь, все это имеет смысл:).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...