Должен ли я использовать shareReplay в качестве последнего оператора? - PullRequest
0 голосов
/ 03 августа 2020

Я разрабатываю приложение Angular 9 и не понимаю, как использовать оператор shareReplay вместе с другими операторами. Я сделал что-то вроде следующего:

if (!this.cache[key]) {
  this.cache[key] = this.http.get(...).pipe(
    shareReplay(1),
    flatMap(...),
    map(...),
    reduce(...)
  );
}
return this.cache[key];

После этого мое приложение зависло со 100% загрузкой ЦП. Когда я изменил его на:

if (!this.cache[key]) {
  this.cache[key] = this.http.get(...).pipe(
    flatMap(...),
    map(...),
    reduce(...),
    shareReplay(1)
  );
}
return this.cache[key];

, похоже, все работает нормально. Обязательно ли использовать последний оператор shareReplay? Откуда такая высокая загрузка ЦП?

Изменить: более подробный фрагмент кода:

this.http.get(...).pipe(
  // I would like to avoid several same http calls
  shareReplay(1),
  // I would like to flatten an array of objects that comes from backend
  flatMap(option => option),
  // I need to map all objects to a format that is acceptable by some library
  map(option => ({
    value: option.key,
    label: option.value
  })),
  // I must reduce it back to an array of the new objects
  reduce((acc: { value: string; label: string }[], option) => {
    acc.push(option);
    return acc;
  }, [])
);

1 Ответ

1 голос
/ 03 августа 2020

Позиция оператора share в конвейере важна, поскольку она определяет, сколько подписчиков подпишутся на операторы, предшествующие share.

Рассмотрим следующий пример:

const interval$ = interval(1000).pipe(
  tap(() => console.log("Interval Triggered")
);

interval$.subscribe();
interval$.subscribe();

Вы увидите, что мы получаем два консольных сообщения каждую секунду. Это имеет смысл, потому что в Rx JS каждый подписчик («потребитель») заставит фабрику создать нового «производителя» (здесь setInterval). В этом случае - нет оператора share - каждый абонент будет go вверх по цепочке операторов и подпишется на предыдущего оператора - пока мы не подпишемся на interval дважды.

Теперь давайте посмотрим at share:

const interval$ = interval(1000).pipe(
  tap(() => console.log("Interval Triggered"),
  share()
);

interval$.subscribe();
interval$.subscribe();

В этой версии мы будем получать только одно сообщение в секунду. Почему?

Потому что share не будет подписываться на его источник, пока есть один активный подписчик. Итак, мы подписываемся на share() дважды, а share() только на interval один раз.

Если мы поменяем позиции, то получим другой результат:

const interval$ = interval(1000).pipe(
  share(),
  tap(() => console.log("Interval Triggered"),
);

interval$.subscribe();
interval$.subscribe();

В этом В этом случае мы дважды подписываемся на tap, а эти два оператора tap дважды подписываются на share. Вот почему мы получаем два консольных сообщения в секунду.

Как показывает опыт, вы, скорее всего, захотите, чтобы ваш share был в конце вашего канала, так как это вызовет операторы, которые идут перед share подписаться только один раз.

Чтобы объяснить потребление ресурсов ЦП: я подозреваю, что вы очень часто подписываетесь на этот Observable - поскольку share не является последним оператором, это означает, что вы повторно подпишетесь на flatMap et c. снова и снова.

...