Убьет ли перезапись моей наблюдаемой переменной текущих подписчиков?

Я хочу иметь возможность кэшировать http-вызов, но также принудительно обновлять кеш. Мой сервис выглядит так:

@Injectable()
export class UserService {
  private currentUser$: Observable<User>;

  constructor(private http: HttpClient) { }

  getCurrentUser(force = false): Observable<User> {
    if (!this.currentUser$ || force) {
      this.currentUser$ = this.http.get<User>(`${environment.API_URL}/profiles/me`)
        .pipe(
          shareReplay(CACHE_SIZE)
        );
    }
    return this.currentUser$;
  }
}

Если я вызову getCurrentUser(true), переменная currentUser$ будет перезаписана. Боюсь, это сотрет всех существующих подписчиков. Это правда? Как мне их сохранить?


person adam0101    schedule 26.09.2018    source источник


Ответы (2)


Представьте, что this.currentUser$ указывает на объект в куче. Ваш метод возвращает копию ссылки на this.currentUser$. Таким образом, все подписанные наблюдатели будут продолжать их слушать (пока все они не отпишутся и Observable не будет собран мусор).

Если вы вызовете метод с «силой», this.currentUser$ просто укажет на другой Observable<User> где-то еще в куче.

person highlysignificantbit    schedule 26.09.2018
comment
Так вы говорите, что у меня будет два Observables в памяти? Один, на который указывает переменная, и один, не имеющий переменной? Вы также говорите, что, поскольку у первого Observable все еще есть подписчики, он не собирает мусор? Похоже на утечку памяти. - person adam0101; 27.09.2018
comment
Кроме того, не будут ли подписчики первого Observable больше получать обновления, поскольку нет переменной, в которую можно было бы передавать новые значения? - person adam0101; 27.09.2018
comment
Это правильные выводы :). Конечно, если вы передадите этот Observable, какой-то другой объект может подтолкнуть новые значения. Но я полагаю, что это не так. Кроме того, вы должны быть осторожны с утечками памяти. Всегда отписывайтесь от ваших Observables. По этому поводу ходит много вопросов/ответов. - person highlysignificantbit; 27.09.2018
comment
Я приму ваш ответ, так как он отвечает на мой вопрос, но не могли бы вы взглянуть на моё решение и сообщить мне если вы видите какие-либо проблемы с ним? - person adam0101; 27.09.2018
comment
У меня недостаточно кредитов, чтобы прокомментировать ваш код ниже :). Но посмотрите на rxjs .take(1) для автоматической отмены подписки после того, как вы что-то получили. - person highlysignificantbit; 27.09.2018
comment
Кроме того, разделите эти методы на два отдельных метода. A) Действие: одно извлекает пользователей и помещает их в тему и B) для «наблюдателей»: метод, возвращающий переменную-член, например userObs$ = currentUser$.asObservable(). - person highlysignificantbit; 27.09.2018
comment
Спасибо. Я разделил методы. Я также удалил логику отказа от подписки для http-вызова. По-видимому, HttpClient делает это за вас - person adam0101; 27.09.2018

Я собираюсь опубликовать то, что я сделал здесь, если это поможет кому-то еще. Вместо этого я возвращаю один экземпляр BehaviorSubject и просто добавляю в него новые значения всякий раз, когда мне нужно «принудительно» получить текущего пользователя. Я также добавил флаг fetchingCurrentUser, чтобы не делать несколько вызовов, пока я жду завершения первого вызова API.

Пожалуйста, дайте мне знать, если у кого-нибудь возникнут какие-либо проблемы с этим или у вас есть идеи, как сделать его чище. Спасибо.

@Injectable()
export class UserService {
  private currentUser$: BehaviorSubject<User>;
  private fetchingCurrentUser: boolean;

  constructor(private http: HttpClient) {
    this.currentUser$ = new BehaviorSubject(null);
  }

  getCurrentUser(force = false): Observable<User> {
    if (this.currentUser$.value == null || force) {
      this.refreshCurrentUser();
    }
    return this.currentUser$.asObservable();
  }

  refreshCurrentUser() {
    if (!this.fetchingCurrentUser) {
      this.fetchingCurrentUser = true;
      this.http.get<User>(`${environment.API_URL}/profiles/me`)
        .subscribe(x => {
          this.currentUser$.next(x);
          this.fetchingCurrentUser = false;
        });
    }
  }
person adam0101    schedule 26.09.2018