Это использование switchMap эквивалентно назначению и подписке? - PullRequest
0 голосов
/ 20 февраля 2020

Я следовал этому Angular руководству по маршрутизации от Google. Он включает в себя класс, который вводит ActivatedRoute как route и HeroService как service. Он имеет следующий метод ngOnInit:

ngOnInit() {
  this.heroes$ = this.route.paramMap.pipe(
    switchMap(params => {
      this.selectedId = +params.get('id');
      return this.service.getHeroes();
    })
  );
}

Пытаясь точно понять, что здесь происходит, я изменил метод ngOnInit следующим образом, и код все еще работает, как и ожидалось:

ngOnInit() {
  this.heroes$ = this.service.getHeroes();

  this.route.paramMap.subscribe( params => {
    this.selectedId = +params.get('id');
  });
}

Я считаю, что мой код легче понять, поскольку два действия, заполняющие this.heroes$ и получающие идентификатор из маршрута, различны; switchMap ощущается как его излишество.

Мой ngOnInit эквивалентен оригиналу?

Я читал, что switchMap возвращает подписку на Observable первого порядка и также отменяет предыдущие подписки. Это то, что мой код не делает, и это то, о чем я должен беспокоиться / беспокоиться?

Я также читал, что мне не нужно отписываться от ActivatedRoute наблюдаемых. Это тот случай, когда мне не нужно отписываться от paramMap в моей версии ngOnInit?

Ответы [ 3 ]

3 голосов
/ 20 февраля 2020

Преобразование моего исходного комментария в ответ с добавленными мыслями ...

Оригинальный комментарий

Первая версия гарантирует порядок обработки. Вызов в службу не будет выполнен, пока не будет возвращен параметр маршрута.

Ваша версия запускает две подписки параллельно. Так что они не эквивалентны. Независимо от того, являются ли они функционально эквивалентными, зависит от отношений между this.heroes$ и this.selectedId.

Если сервисный вызов зависел от параметра маршрута, вам пришлось бы использовать первый подход.

Обновление

Проверка привела к этому думать о небольшой, но важной разнице. Первая версия делает дублирующие сервисные звонки. Наблюдаемая paramMap будет срабатывать каждый раз при обновлении параметра маршрута. Ниже приведен один пример того, как это могло бы произойти.

ngOnInit(): void {
  this.route.paramMap.subscribe(params => {
    // this will be called every time the selectHero function is called
    this.selectedId = params.get('id');
  });

  this.heroes$ = this.service.getHeroes();
}

// Event handler, called from the HTML
selectHero(id: string): void {
  // Triggers emission of a new param value in route.paramMap
  this.router.navigate(['/heroes', { id: id }]);
}

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

Я читал, что switchMap возвращает подписка на Observable первого порядка, а также отменяет предыдущие подписки. Это то, что мой код не делает, и это то, о чем я должен беспокоиться / беспокоиться?

switchMap не отменяет подписку на внешнюю наблюдаемую, она отменяет внутреннюю наблюдаемую при выдаче нового значения , Я создал простой стек-блиц для демонстрации этого.

Перейдите на страницу «прочее» и нажмите одну из ссылок «Один», «Два», «Три». Каждый щелчок обновит параметр маршрута, и вы увидите журнал, который из подписок все еще работает.

Слишком много кода для вставки, но он устанавливает следующие надписи:

const p$ = this.route.paramMap;
p$.subscribe(params => {});
p$.pipe(take(1)).subscribe(params => {});
p$.pipe(switchMap(params => of(params.get('id')))).subscribe(id => {});
p$.pipe(takeUntil(this.timerElapsed)).subscribe(params => {});

Подписки 1 и 3 будут работать в течение всего срока службы компонента. Это показывает, что наличие switchMap в вашем первом примере не приведет к автоматической отмене подписки, если вы к этому добились.

Я также читал, что мне не нужно отписаться ActivatedRoute наблюдаемые. Это тот случай, когда мне не нужно отписываться от paramMap в моей версии ngOnInit?

Существует очень мало официальной документации по этому вопросу, которую я могу найти. Это часто повторяемое утверждение из авторитетных источников. Эта относительно недавняя статья повторяет утверждение.

Нет необходимости отписываться от активированного маршрута.

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

Заключение

  1. Приведение в порядок наблюдаемых

Нет разницы между вашими примерами. Вам не нужно убирать ActivatedRoute подписок, и не нужно было бы убирать наблюдаемую услугу, если она от HttpClient.

Порядок звонков

Ваш первый пример гарантирует порядок, второй - нет. Это вряд ли окажет практическое влияние, поскольку маршрутизатор почти наверняка вернет параметр, прежде чем служба вернет результат (при условии запроса XHR).

Количество сервисных вызовов

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

Я еще раз рассмотрел это, потому что это более интересный вопрос, который я изначально предполагал. Я рад, что я сделал, и я призываю вас медленно исследовать мощный и запутанный мир Rx JS.

0 голосов
/ 20 февраля 2020

Первая версия ведет к сохранению свойства Observable в heroes, вторая версия - к объекту или объекту Observable.

Первая версия гарантирует порядок выполнения. Эта строка выполняется первой this.selectedId = +params.get('id');

Во второй версии вы должны отписаться paramMap.

0 голосов
/ 20 февраля 2020

Оператор switchMap гарантирует порядок обработки. Вызов в службу не будет выполнен до тех пор, пока не будет возвращен маршрут param.

Без switchMap две подписки будут работать параллельно.

Если вызов службы зависел в параметре маршрута вы должны будете использовать первый подход.

...