Каков правильный оператор rxjs в случае вложенной подписки с оператором if

У меня есть 2 подписки в проекте angular/typescript.

Эти 2 подписки подписываются на каждый из своих наблюдаемых A и B, которые находятся вне компонента и в сервисном файле.

Иногда A меняется, а B может не изменяться в зависимости от действия какого-либо компонента и наоборот. Однако обычно B изменяется после A изменений в другом компоненте.

Кроме того, B должен что-то сделать после проверки правильного значения A . Поэтому все, о чем я могу думать, это вложенная подписка с оператором if, как показано ниже.

ngOnInit(){
    this.A = this.serviceX.a.subscribe(
      data =>{ 
        this.temp = data;
        if (data=='correct'){
            this.B = this.serviceX.b.subscribe(
                beta => {
                    console.info('Sure'+this.temp);
                }
            )
        }
      }
    )
}

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

Так что теперь все, о чем я могу думать, это отдельная подписка и подписка на них одну за другой, как показано ниже.

this.A = this.serviceX.a.subscribe(
  data =>{ 
    this.temp = data ;
  }
)
this.B = this.serviceX.b.subscribe(
  beta =>{ 
    if (this.temp=='correct'){
        console.info('Sure'+this.temp)
    }
  }
)

Но я очень обеспокоен тем, что делаю это неправильно и делаю нестабильную итерацию. Что такое оператор rxjs, который я могу использовать, чтобы убедиться, что подписка B подписывается только после того, как подписка A будет изменена (однако A, возможно, не будет изменена некоторое время)? И в конечном итоге сделать всю процедуру более надежной?

Как/где вы используете this.A и this. B?

wlf 28.12.2022 09:52
this.A и this.B объявлены только для того, чтобы я мог unsubscribe() их позже.
AkiZukiLenn 28.12.2022 09:57

А как насчет this.temp? Он используется только для проверки на B? Где и как вы на самом деле используете значения, полученные из A и B?

wlf 28.12.2022 10:03

Да this.temp используется только для проверки B. Я использую значение, полученное из A и B, для изменения другой переменной в том же классе. Проблема в том, что B нужно дождаться окончания A, если бы A были какие-либо изменения для правильного ответа. Однако в некоторых случаях A может вообще не измениться, а изменится только B.

AkiZukiLenn 28.12.2022 10:04

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

wlf 28.12.2022 11:19
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
5
90
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Возможно, вы ищете flatMap, вы можете сопоставить несколько подписок, каждая flatMap должна возвращать свою собственную Observable и подписываться только в конце.

this.serviceX.a.pipe(flatMap(data => {
  this.temp = data;
  if (data == 'correct') {
    return this.serviceX.b.pipe(
      flatMap(beta => {
        this.temp2 = beta;
        console.info('Sure' + this.temp);
        return this.serviceX.c.pipe(map(delta => {
          this.temp3 = delta;
          console.info('Sure' + this.temp2);
          return delta;
        }));
      }));
  }
})).subscribe(delta => {
  console.info('Complete all subscriptions');
});
flatMap устарел и переименован в mergeMap: rxjs.dev/api/operators/flatMap
churill 28.12.2022 09:59
Ответ принят как подходящий

Подписка внутри обработчика подписки может привести к путанице, и я считаю, что ее можно и нужно всегда избегать. Ваш основной рабочий процесс - как я понял ваш код - таков:

  1. получить данные из serviceX.a
  2. хранить данные как побочный эффект
  3. если данные удовлетворяют какому-либо условию, получить данные из serviceX.b
  4. фактический обработчик подписки

Теперь вы можете найти подходящий оператор для каждого шага. Вот как бы я это сделал:

this.serviceX.a.pipe(                 // 1 - load from a
  tap(data => this.temp = data),      // 2 - side effect
  filter(data => data == 'correct'),  // 3a - if
  concatMap(() => this.serviceX.b)    // 3b - load from b
).subscribe((beta) => 
  console.info('Sure' + this.temp)     // 4 - actual subscribe
); 

Примечание 1. Здесь вам нужна только одна подписка.

Примечание 2. В зависимости от вашего варианта использования вы можете заменить concatMap на switchMap , ExhaustMap или mergeMap.

Примечание 3: использование temp здесь довольно опасно. В зависимости от того, какой *map-оператор вы выберете, вы можете создать состояние гонки. Пока вы ждете выброса serviceX.bserviceX.a уже может выдать следующее значение и перезаписать temp. Если единственная причина для temp — сохранить результат из serviceX.a, вместо этого вам следует использовать это решение: Есть ли оператор, позволяющий сохранить начальное значение подписки после выполнения ,,switchMap"?

Просто хочу подтвердить, иногда A может не быть изменений и, следовательно, никакой next() операции, испускающей все такое, вы уверены, что B все еще будет работать, если только B что-то изменить?

AkiZukiLenn 28.12.2022 10:15

@AkiZukiLenn Нет, не будет. Извините, это не было ясно из вашего вопроса. Вы сказали: «Иногда A изменяется, а B может быть не [...]», но вы также сказали: «Кроме того, B должен что-то сделать после проверки правильного значения A» (я интерпретирую B работает после A). Теперь мне не совсем понятно, что должно происходить в каком порядке. Оба могут измениться, но B должен проверить значение A. Так что же должно произойти, если A не излучает? Что должен проверить Б? И как вы проверяете, не излучает ли A, или, может быть, он просто излучает после B из-за неудачи?

churill 28.12.2022 14:42

@AkiZukiLenn tldr; Я был бы рад предложить решение, но вижу, что не до конца понял ваш вопрос. Нам может понадобиться больше деталей, и вы должны разработать план, когда что должно произойти :)

churill 28.12.2022 14:43

1. A излучает, B «может» не излучает, и на этом операция заканчивается. 2. Теперь, если после того, как A излучает, B также излучает, то B должен дождаться, пока A закончит излучение, чтобы A вернул окончательный ответ. 3. Кроме того, после того, как A излучает, а B излучает, B может снова излучаться, а A ничего не излучает. Таким образом, есть то, что «иногда может случиться иногда не путать». Это делает мой вопрос более ясным?

AkiZukiLenn 29.12.2022 04:29

Я бы сказал, что B должен испускаться после A, поскольку логически пользователь может получить доступ только к A до B. По крайней мере, с точки зрения маршрутизации. Итак, как только пользователь нажимает ссылку A, A регистрируется и излучает. Затем, основываясь на значении, испускаемом A, B также может излучать. Хотя B может излучать много раз, но A ничего не излучает, так как A уже излучается. Таким образом, я не могу понять, как я могу использовать оператор filter rxjs.

AkiZukiLenn 29.12.2022 04:42

@AkiZukiLenn Честно говоря, это кажется довольно запутанным дизайном. Мой ответ должен работать, если ваш второй комментарий верен и B может излучаться только после A. Иногда полезно использовать ReplaySubject или BehaviorSubject для обработки просроченных подписок, надеюсь, у вас это получится ;)

churill 30.12.2022 15:40

Да, дело в том, что он отлично работает, если я делаю их обоими BehaviorSubject, он работает нормально. Мне просто интересно, есть ли оператор лучше.

AkiZukiLenn 31.12.2022 09:50

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

Затем вам нужно продолжать управлять своим Стримом оператором mergeMap или switchMap и вызывать только один Subscribe в конце.

Для фильтрации потока вы можете использовать оператор filter.

this.serviceX.a.pipe(                 
  filter((data: any) => data == 'correct'),  
  switchMap((value: any) => this.serviceX.b)     
)
.subscribe((value: any) => 
  console.info('Sure' + this.temp);     
); 

После этого у вас останется только 1 подписчик, от которого нужно отписаться.

Другие вопросы по теме