Угловая 2 наблюдаемая подписка дважды выполняет вызов дважды

проблема
Я подписываюсь на HTTPClient.get, наблюдаемый дважды. Однако это означает, что мой вызов выполняется дважды. Почему это?

доказательство
Для каждой подписки() я делаю, я получаю другую строку на странице входа.

Код (onSubmit() от кнопки входа в систему)

var httpHeaders = new HttpHeaders()
  .append("Authorization", 'Basic ' + btoa(this.username + ':' + this.password));

var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders});
observable.subscribe(
  () => {
    console.log('First request completed');
  },
  (error: HttpErrorResponse) => {
    console.log('First request error');
  }
);
observable.subscribe(
  () => {
    console.log('Second request completed');
  },
  (error: HttpErrorResponse) => {
    console.log('Second request error');
  }
);

Консоль

zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:54 First request error
zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:62 First request error

Неприемлемый фон
У меня есть объект LogonService, который обрабатывает все мои функции входа. Он содержит логическую переменную, которая показывает, вошел ли я в систему или нет. Всякий раз, когда я вызываю функцию входа в систему, он подписывается на наблюдаемый httpClient.get, чтобы установить переменную входа в true или false. Но функция входа также возвращает наблюдаемый, на который подписывается. Мне потребовалось некоторое время, чтобы связать двойной запрос с двойной подпиской. Если есть лучший способ отслеживания логина, чем через переменную, дайте мне знать! Я пытаюсь научиться угловатым :)

Ответ 1

Вы можете использовать оператор share для своего результата из HttpClient.get, например:

var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders })
  .pipe(share());

Вам нужно добавить следующий импорт поверх вашего скрипта:

import { share } from 'rxjs/operators';

Оператор share создает наблюдаемый hot, то есть разделяемый между подписчиками. Но есть еще кое-что, я бы предложил эту статью углубиться (вы можете, конечно, также погуглить hot vs cold observables, чтобы найти больше).

Ответ 2

Ваша наблюдаемая холодна:

Наблюдаемое является холодным, если создатель его уведомлений создан всякий раз, когда наблюдатель присоединяется к наблюдаемому. Например, наблюдаемый таймер холодный; каждый раз, когда подписка сделана, новый Таймер создан.

Вам нужно multicast ваш Observable или, другими словами, чтобы он был горячим:

Наблюдаемое является горячим, если производитель его уведомлений не создается каждый раз, когда наблюдатель подписывается на наблюдаемое. За например, наблюдаемая, созданная с помощью fromEvent, является горячей; элемент который производит события, существует в DOM - он не создается, когда наблюдатель подписан.

Для этого вы можете использовать оператор share, но он по-прежнему не может гарантировать вам один HTTP-вызов. Доля будет multicast вашей наблюдаемой, делая ее общей для подписчиков, но после завершения http-вызова она сделает новый http-вызов для новых подписчиков.

Если вы хотите использовать режим кэширования (выполнение вызова один раз, а затем предоставление значения каждому подписчику при каждой подписке), вам следует использовать publishReplay().refCount().

Дальнейшее чтение:

Опубликуйте и поделитесь операторами

Ответ 3

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

export class App implements OnInit {

cars$: Observable<Car[]>;

constructor(private carsService: carsService) {

}

ngOnInit() {
    this.lessons$ = this.lessonsService.loadLessons().publishLast().refCount();

    this.lessons$.subscribe(
         () => console.log('lessons loaded'),
         console.error
         );
    }
}

Угловая документация.